Repository navigation
refactor(terraform): draw the foundation/service seam - #7
Merged
Merged
Conversation
The boundary doc assumed one terraform copy per consumer. That premise is wrong: frontend and other services share this infrastructure. With more than one consumer, resources split into two layers with very different change rates and blast radius, and the code mixed them. Sharing cost is not one number. In ap-northeast-2 at 730h, VPC/subnets/ ECS cluster are free, ALB is $16.43, Valkey $14.02 and RDS $18.25 per month. Complexity differs by an order of magnitude in the other direction: a shared RDS with least-privilege users needs a SQL provider that must reach a private-subnet instance, so it buys a permanent privileged runner inside the VPC to save $18/month. The data plane stays per-service; the foundation shares what is free plus the ALB. - name axis: local.name was the name of everything from the VPC to the target group, so two services would collide on the ECS cluster name alone. Split into local.foundation_name and local.service_name, with a new service_name input. - security groups: inline ingress/egress blocks are authoritative, so once the layers split a foundation apply would delete rules a service attached to a shared group. Moved to aws_vpc_security_group_*_rule. - outputs: none of the identifiers a service root needs existed. Added vpc_id, subnet ids, ALB security group id, listener arn, cluster arn and db subnet group name as the foundation contract. - Redis namespaces: the app already has REDIS_DB, REDIS_KEY_PREFIX and BULLMQ_PREFIX; terraform never passed them, so two services on one Valkey would both use 'app:' on db 0. Derived from service_name. - names silently truncated at 32 characters via substr(); two long service names could collide and only show it at apply. Replaced with lifecycle preconditions that fail during plan. - backend.hcl.example documents the foundation/service key convention. State boundaries come from the backend key, not from the repository. - removed the unused local.redis_on. Verified against real AWS: terraform plan reports 33 to add, 0 to change, 0 to destroy — the first successful plan of this config. Not applied, so task startup, health checks and the env contract remain unverified. ALB routing is still undecided. The listener default action currently forwards straight to this service's target group, which stops working with a second service. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DrsDBRXt7sYCYxt5gmFMVy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
왜
결정 문서가 "terraform 사본 하나당 소비자 하나"를 전제했는데 그 전제가 틀렸다. frontend와 다른 서비스가 같은 인프라를 나눠 쓴다. 소비자가 여럿이면 리소스가 변경 빈도와 blast radius가 전혀 다른 두 층으로 갈리는데, 코드는 둘을 섞고 있었다.
저장소 경계보다 이 seam이 먼저다. state 경계는 저장소가 아니라 backend key가 정한다 — 저장소를 옮겨도 key가 같으면 같은 state이고, 안 옮겨도 key를 나누면 나뉜다.
공유 비용은 하나가 아니라 셋이다
ap-northeast-2, 730h, AWS 공식 Price List (storage·LCU 제외):공유 RDS의 최소권한은
hashicorp/aws만으로 불가능하다.CREATE ROLE/GRANT는 SQL catalog 작업이라cyrilgdn/postgresql같은 provider가 필요한데, RDS는data.tfpublicly_accessible = false에 private subnet이고 data SG는 app SG만 허용해서 GitHub 호스티드 runner가 닿지 못한다. $18/월을 아끼려고 영구적인 특권 CI 표면을 사는 거래다. 안 사면 대안은 모든 서비스에 master credential 배포다.→ 데이터 플레인은 서비스별, foundation은 공짜인 것들 + ALB만 공유.
변경
local.name하나가 VPC부터 target group까지 전부의 이름 →foundation_name/service_name분리,var.service_name신규ingress(authoritative) →aws_vpc_security_group_*_rule. 층이 갈리면 foundation apply가 service의 rule을 지운다app:/db 0 충돌 → service name에서 유도substr(..., 0, 32)조용한 절단 →lifecycle.precondition으로 plan에서 실패검증
실제 AWS 대상 plan이 처음으로 통과했다:
validation/precondition 4종 발화 확인:
service_name=APImust be lowercase alphanumeric with hyphens...redis_db=16must be an integer between 0 and 15.project_name=verylongprojectnamehere+environment=productionALB name '...' exceeds 32 charactersservice_name=averyveryverylongservicenameTarget group name '...' exceeds 32뒤 둘이 이전엔 조용히 잘리던 것들이다.
그 외:
fmt -check -recursive/init -backend=false/validate,check:scaffold(typeorm + postgres),check:observability,typecheck,lint,pnpm test→Test Files 35 passed (35) / Tests 317 passed (317).미검증 / 남은 것
PriorityInUse) 대역 배정 규약이 함께 필요하다buildRedisOptions(src/config/redis.config.ts)가 password만 받고 username/TLS가 없어 ElastiCache RBAC를 못 쓴다 — 앱 변경 선행교차검증: Claude(주 세션)와 Codex(orca 워크트리
infra-seam-audit, gpt-5.6-terra high) 2라운드. 읽기 전용.🤖 Generated with Claude Code
https://claude.ai/code/session_01DrsDBRXt7sYCYxt5gmFMVy