208 lines
22 KiB
Markdown
208 lines
22 KiB
Markdown
---
|
|
title: ca-tmpl - Clean Architecture 패키지 레이아웃 결정
|
|
source_type: project
|
|
status: verified
|
|
confidence: high
|
|
tags: [ca-skeleton, clean-architecture, package-layout, locally-verified, interview-candidate]
|
|
related_projects: [ca-skeleton, ca-tmpl]
|
|
last_reviewed: 2026-07-02
|
|
---
|
|
|
|
# ca-tmpl - Clean Architecture 패키지 레이아웃 결정
|
|
|
|
> Layer: `wiki/projects/` — 내 프로젝트 사실. 일반 개념은 `wiki/concepts/clean-architecture-package-layout` 사용.
|
|
|
|
## 프로젝트 컨텍스트
|
|
|
|
ca-tmpl은 Java 21 + Spring Boot 3.4 + Gradle multi-module 기반 Clean Architecture skeleton template이다. `blog` 도메인은 reference implementation이며, 새 프로젝트에서는 도메인 이름과 엔티티를 교체하되 module boundary와 dependency direction은 유지한다.
|
|
|
|
본 문서는 `feature-skeleton-package-blueprint-contract` branch-note의 package/module blueprint가 ca-tmpl repo에 실제 반영된 상태를 기록한다. 이 slice는 `actually-implemented` + `locally-verified`이며, 운영 배포 대상이 아니므로 `prod-verified`는 없다. 2026-06-04 ca-tmpl 레포(`@5d89766`) ground-truth 대조로 아래 사실을 검증함 (§Ground-truth 대조 참조).
|
|
|
|
## 실제 구현 내용 (`actually-implemented`)
|
|
|
|
- Gradle include가 `app-bootstrap`, `domain-core`, `application-core`, `adapter-web`, `adapter-persistence`, `adapter-outbound`, `shared-contract`, `sample-portfolio` 8개 module로 전환되었다. (이후 `feature-resource-identifier-contract` branch가 9번째 module `adapter-identifier`를 추가했으나 이는 본 slice 범위 밖이다.)
|
|
- production package root는 `dev.caskeleton`이다. 기존 reference blog code는 다음 mapping으로 이동했다.
|
|
- `cmd` → `app-bootstrap` / `dev.caskeleton.bootstrap` (`BlogApplication` → `CaSkeletonApplication`)
|
|
- `domain` → `domain-core` / `dev.caskeleton.domain`
|
|
- `service` → `application-core` / `dev.caskeleton.application`
|
|
- `presentation` → `adapter-web` / `dev.caskeleton.adapter.web`
|
|
- `infra` → `adapter-persistence` / `dev.caskeleton.adapter.persistence`
|
|
- `blog.*` 설정 prefix → `ca-skeleton.*`, `CmdSettings` → `BootstrapSettings`
|
|
- 기존 reference code는 production module에서 격리되어 `sample-portfolio` 내부 `dev.caskeleton.sample.portfolio.{domain,application}.worklog` package로 이동했다.
|
|
- `domain-core`, `application-core`, `adapter-persistence`, `adapter-outbound`, `shared-contract`는 skeleton anchor package + `package-info.java` 중심으로 유지된다. 각 module 내부의 실제 business/contract type 구현은 후속 branch slice들(`feature-operational-error-observability-foundation`, `feature-api-contract-baseline` 등)이 채운다.
|
|
- `src/build.gradle`의 `verifyCleanArchitectureDependencies` task(root `build.gradle:53`)가 module dependency matrix를 검사한다.
|
|
- `app-bootstrap`의 `CleanArchitectureTest`(ArchUnit)가 domain purity, application adapter isolation, adapter 간 직접 의존 금지, web DTO containment, shared-contract package scope, production → `sample-portfolio` dependency 금지를 검사한다.
|
|
- **(D9) module 간 의존 선언 정책**: 기본 `implementation`, 소비자의 public ABI에 타 module 타입이 노출될 때만 `api`. ground-truth 확인: 9개 `build.gradle` 모두 `api` 선언 0개, 전부 `implementation` — 정책 충족. 근거 `raw/official-docs/gradle-java-library-api-vs-implementation.md`.
|
|
- **(D10) `@SpringBootApplication` 배치**: `dev.caskeleton.bootstrap`(root package)에 두고 default package 금지. multi-module component scan을 위해 `@SpringBootApplication(scanBasePackages = "dev.caskeleton")` 명시. ground-truth 확인: `app-bootstrap/.../bootstrap/CaSkeletonApplication.java`에 일치. 근거 `raw/official-docs/spring-boot-structuring-your-code.md`.
|
|
- `README.md`, `AGENTS.md`, root/module `CLAUDE.md`, local clean-architecture rule이 새 module vocabulary로 갱신되었다.
|
|
|
|
### Boundary enforcement rules (`feature-architecture-enforcement-rules` slice)
|
|
|
|
> 이 sub-section은 module/package *blueprint* 위에 얹는 **enforcement-rules dimension**이다. 위 blueprint가 "module 경계가 어디 있는가"라면, 아래는 "그 경계가 깨지면 build가 실패하는가"를 다룬다. ca-tmpl `@db61075` ground-truth 대조로 아래 rule 이름·개수·위치를 확인했다(§Ground-truth 대조 — enforcement 참조). ⚠️ ground-truth 파일은 이후 다른 branch slice들이 rule을 더 추가했으므로, 아래는 **본 enforcement-rules slice가 정의·구현한 항목만** 추렸다(타 slice rule은 해당 branch ingest에서 다룬다).
|
|
|
|
ArchUnit test(`app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/CleanArchitectureTest.java`, `@AnalyzeClasses(packages = "dev.caskeleton", importOptions = DoNotIncludeTests.class)`)가 정적 import/dependency graph를 검사한다. 본 slice가 정의한 rule:
|
|
|
|
- `domain_is_pure` — `..domain..`이 `org.springframework..` / `jakarta.persistence..` / `javax.persistence..` / `jakarta.servlet..` / `org.hibernate..` / **`lombok..`**(D3) / `..application..` / `..adapter..` / `..bootstrap..` 등에 의존하면 실패. domain을 framework-neutral POJO로 유지. (`actually-implemented`)
|
|
- `application_does_not_depend_on_adapters_or_transport` — `..application..`이 `..adapter..` / `..bootstrap..` / `org.springframework.web..` / persistence·hibernate에 의존하면 실패. (`actually-implemented`)
|
|
- `application_does_not_use_spring_transactional_annotation` — `..application..`이 `org.springframework.transaction.annotation.Transactional` FQN에 의존하면 실패. (코드 주석상 attribution은 `feature-application-port-usecase-contract D3`이나, 본 enforcement slice의 테스트 계약에도 포함되어 `locally-verified`로 red/green 확인됨.)
|
|
- `application_does_not_depend_on_application_context` (**D11**, banned-class rule) — `..application..`이 `org.springframework.context.ApplicationContext` FQN에 의존하면 실패. class-literal 기반 `getBean(Class<T>)` 호출까지는 bytecode access로 catch. (`actually-implemented`) — **한계(D12)**: string-key `getBean(String)`·`Class.forName(String)`·`BeanFactory#getBeansOfType` 같은 reflection-style bypass는 ArchUnit 정적 분석으로 catch 불가. `application-core/CLAUDE.md` forbidden 섹션의 code review checklist로만 보완. ArchUnit이 모든 우회를 잡는다고 말하면 과장.
|
|
- `web_adapter_does_not_depend_on_persistence_or_outbound_adapters` / `persistence_adapter_does_not_depend_on_web_or_outbound_adapters` / `outbound_adapter_does_not_depend_on_web_or_persistence_adapters` — adapter module 간 직접 의존 금지. (`actually-implemented`)
|
|
- `web_dtos_stay_in_web_adapter` — `..adapter.web..dto..`는 `..adapter.web..`에서만 접근 가능(DTO containment). (`actually-implemented`)
|
|
- `shared_contract_contains_only_operational_contract_packages` — `..shared..`는 response/request/error/operation/headers/logging/tracing/metrics/registry/annotation operational-contract package allowlist만 허용; business/domain concept 유입 시 실패. (`locally-verified` — 임시 `shared.worklog` 위반으로 red 확인)
|
|
- `production_code_does_not_depend_on_sample_portfolio` — `..sample.portfolio..` 밖 production code가 sample package에 의존하면 실패. (`locally-verified`)
|
|
|
|
Gradle build-graph 검사는 `verifyCleanArchitectureDependencies` task(`src/build.gradle:53`, root)가 담당한다. `allowedProjectDependencies` matrix로 9개 module의 허용된 `project()` dependency(`api`/`implementation`/`compileOnly`/`runtimeOnly`)를 화이트리스트하고, 허용 외 `ProjectDependency`가 선언되면 `GradleException`을 던진다. ArchUnit이 *source import graph*를, 이 task가 *Gradle project dependency graph*를 막는 이중 방어다. (`actually-implemented` — task 존재 + matrix; `locally-verified` — 임시 `app-bootstrap → sample-portfolio` 선언으로 red 확인)
|
|
|
|
`allowEmptyShould(true)`: 대부분 rule이 빈 anchor module(아직 구현 type이 없는 module)에서 vacuous하게 통과하지 않도록 명시. 빈 should가 곧 PASS로 둔갑하는 ArchUnit empty-should anchor 문제를 다루기 위함.
|
|
|
|
### Negative fixture (violations-as-data)
|
|
|
|
`ArchitectureViolationFixtureTest`(같은 `architecture/` 패키지)가 본 slice의 각 rule이 *실제로* 위반을 catch하는지 commit된 negative test로 보증한다(Spring Modulith `example/ninvalid` 패턴 차용). 본 slice가 추가한 fixture·test(round 2, 2026-05-28): `SpringDependentDomainFixture`(domain_is_pure D3), `ApplicationContextDependentFixture`(D11), `TransactionalAnnotatedFixture`(@Transactional)를 포함한 의도된 위반 class와, 대응 `*_catches_violation` test가 `rule.evaluate(VIOLATION_CLASSES).hasViolation() == true`를 assert한다. fixture는 `src/test/...`에 위치하므로 main `@AnalyzeClasses(importOptions = DoNotIncludeTests.class)` 분석에서 제외 → main suite의 vacuous pass 위험 없음. (`actually-implemented`)
|
|
|
|
> 이후 branch slice들이 같은 fixture tree에 boundary-validation·streaming·serialization·resource-identifier·api-contract rule용 fixture를 추가해, 현재 ground-truth `ArchitectureViolationFixtureTest`는 본 slice 범위를 넘는 negative test를 다수 포함한다. 본 doc은 enforcement-rules slice가 만든 fixture만 위에 명시했다.
|
|
|
|
## 로컬/dev 검증 (`locally-verified`)
|
|
|
|
2026-05-27 ca-tmpl repo에서 다음 명령이 통과했다.
|
|
|
|
```bash
|
|
cd src
|
|
./gradlew verifyCleanArchitectureDependencies
|
|
./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest'
|
|
./gradlew :adapter-web:test --tests '*SettingsTest'
|
|
./gradlew test
|
|
```
|
|
|
|
검증 의미:
|
|
|
|
- Gradle project dependency graph가 branch-note의 module dependency direction을 위반하지 않는다.
|
|
- ArchUnit이 source-level forbidden dependency를 검사한다.
|
|
- web settings binding tests가 package rename 이후에도 통과한다.
|
|
- 전체 Gradle test suite가 새 module layout에서 통과한다.
|
|
|
|
enforcement-rules slice 추가 red/green 검증(2026-05-28, `feature-architecture-enforcement-rules`):
|
|
|
|
```bash
|
|
cd src
|
|
./gradlew :app-bootstrap:test verifyCleanArchitectureDependencies # ArchUnit + Gradle graph
|
|
./gradlew check # 전체 — round 2 fixture 포함
|
|
```
|
|
|
|
- 임시 위반 코드(`application @Transactional`, controller domain return, mapper → application 의존, `shared.worklog` package)를 추가했을 때 `CleanArchitectureTest`가 실패함을 확인한 뒤 임시 파일을 제거했다.
|
|
- 임시 `app-bootstrap → sample-portfolio` project dependency 선언 시 `verifyCleanArchitectureDependencies`가 실패함을 확인한 뒤 제거했다.
|
|
- round 2: `domain_is_pure`의 `lombok..` 추가(D3)와 `application_does_not_depend_on_application_context`(D11)가 commit된 negative fixture(`ArchitectureViolationFixtureTest`)로 catch 동작을 보증함을 확인했다.
|
|
|
|
## 운영 검증 (`prod-verified`)
|
|
|
|
없음. ca-tmpl은 template repository이며, 이번 package blueprint slice는 운영 배포/운영 로그/운영 metric으로 검증된 항목이 아니다.
|
|
|
|
## 문서/계획만 존재 (`documented-only` / `planned`)
|
|
|
|
> 이 등급은 **본 blueprint slice 시점(2026-05-27)** 기준이다. 일부 항목은 이후 별도 branch slice가 구현했을 수 있으며, 그 검증은 해당 branch의 ingest에서 갱신한다(본 slice는 module/package *경계*만 검증).
|
|
|
|
- `sample-portfolio` 실제 worklog domain fixture business flow는 본 slice 시점엔 anchor 중심이었다. (현재 `dev.caskeleton.sample.portfolio.{domain,application}.worklog`에 worklog 모델/테스트 존재 — 별도 sample fixture branch 산출물.)
|
|
- `adapter-outbound` 실제 HTTP client/messaging/cache/notification adapter는 본 slice 시점에 미구현(anchor만).
|
|
- `shared-contract` 실제 response/error/header/logging/tracing/metrics/registry/annotation type은 본 slice 시점에 미구현(anchor만). 이후 `feature-operational-error-observability-foundation`·`feature-api-contract-baseline` slice가 일부 채움.
|
|
- `application/port/in` 및 `application/port/out` package anchor는 존재하지만, reference blog repository port는 본 slice 시점엔 `sample-portfolio/domain/repository`에 남아 있었다. production use case port 정리는 `feature-application-port-usecase-contract` branch에서 수행한다.
|
|
- Spring Modulith verifier는 도입하지 않았다. 현재 검증은 Gradle dependency rule + ArchUnit rule이다.
|
|
|
|
## 면접에서 말할 수 있는 범위
|
|
|
|
- **자신 있게 답할 수 있는 질문**
|
|
- 왜 Gradle multi-module을 1차 boundary로 두고 `domain-core` / `application-core` / `adapter-*`를 물리 분리했는지.
|
|
- `shared-contract`를 business common dumping ground로 쓰지 않기 위해 어떤 package와 ArchUnit rule을 두었는지.
|
|
- `sample-portfolio`이 presentation layer가 아니라 fixture/sample consumer module인 이유.
|
|
- `verifyCleanArchitectureDependencies`와 ArchUnit test가 각각 build graph와 source import graph에서 무엇을 막는지.
|
|
|
|
- **적당히 답할 수 있는 질문**
|
|
- 왜 Spring Modulith를 즉시 도입하지 않았는지.
|
|
- reference blog port가 아직 `domain/repository`에 남아 있는 이유와 `feature-application-port-usecase-contract` branch에서 `application/port/out`으로 이동할 계획.
|
|
|
|
- **답하면 안 되는 질문**
|
|
- “운영에서 검증했다”는 표현. 운영 배포/운영 metric 근거가 없다.
|
|
- “sample-portfolio worklog business flow까지 이 blueprint slice에서 구현했다”는 표현. 본 slice는 module/package 경계만 검증했고, fixture 구현은 별도 slice다.
|
|
- “ArchUnit이 모든 boundary 우회를 잡는다”는 표현. runtime lookup/reflection 우회는 별도 리뷰와 CI 보완이 필요하다.
|
|
|
|
## 과장 금지 지점
|
|
|
|
- “ca-tmpl 전체 Phase C2가 완료됐다” → 금지. package/module blueprint slice만 local verification 완료.
|
|
- “모든 operational contract가 구현됐다” → 금지. registry/generated constants, outbox, security, runtime, privacy 등은 별도 slice다.
|
|
- “Spring Modulith 수준 named interface 검증을 구현했다” → 금지. 현재는 Gradle + ArchUnit 최소 검증이다.
|
|
- “prod-verified” → 금지. 운영 환경 검증 없음.
|
|
- “`application_does_not_depend_on_application_context`(D11) rule이 모든 Spring container 우회를 잡는다” → 금지. class-literal `getBean(Class)`까지만 catch하고, string-key `getBean(String)` / `Class.forName(String)` / `BeanFactory#getBeansOfType` reflection-style bypass는 ArchUnit 정적 분석 범위 밖이다(D12). 이 부분은 code review checklist로만 보완하며 자동 강제 장치가 아니다.
|
|
- “ArchUnit/Gradle이 enforcement-rules의 모든 항목을 자동 검증한다” → 금지. MapStruct generated mapper exemption(D9)은 `needs-confirmation`, runtime lookup false-pass 확인은 `planned`로 남아 있다.
|
|
|
|
## Ground-truth 대조 (2026-06-04, ca-tmpl `@5d89766`)
|
|
|
|
실제 레포 대조로 검증한 사실 (`locally-verified`):
|
|
|
|
| 검증 항목 | ca-tmpl 증거 |
|
|
|---|---|
|
|
| 8 module include | `settings.gradle` 일치 (+ `adapter-identifier`는 별도 branch) |
|
|
| production root `dev.caskeleton` | `CaSkeletonApplication` @ `dev.caskeleton.bootstrap` |
|
|
| D9 전 module `implementation` | 9개 `build.gradle` 모두 `api` 0개 |
|
|
| D10 `scanBasePackages="dev.caskeleton"` | `CaSkeletonApplication.java` |
|
|
| boundary guardrail | root `build.gradle:53` `verifyCleanArchitectureDependencies` + `app-bootstrap/.../architecture/CleanArchitectureTest.java` |
|
|
| anchor `package-info.java` | domain-core·shared-contract·adapter-* 존재 |
|
|
| sample 격리 | `dev.caskeleton.sample.portfolio.*.worklog` |
|
|
|
|
**대조에서 정정된 1차 추출 오류**: 기존 문서의 `com.example.blog.*`(→ `dev.caskeleton.*`), `sample-ticket`(→ `sample-portfolio`)은 1차 추출 시점의 stale 값이었고 본 ingest에서 ground-truth로 정정함.
|
|
|
|
### Enforcement-rules dimension 대조 (2026-06-04, ca-tmpl `@db61075`)
|
|
|
|
`feature-architecture-enforcement-rules` slice가 정의한 항목만 실제 레포와 대조함 (`actually-implemented` / `locally-verified`):
|
|
|
|
| 검증 항목 | ca-tmpl 증거 |
|
|
|---|---|
|
|
| ArchUnit suite 진입점 | `app-bootstrap/.../architecture/CleanArchitectureTest.java`, `@AnalyzeClasses(packages = "dev.caskeleton", importOptions = DoNotIncludeTests.class)` |
|
|
| domain purity + Lombok ban (D3) | `domain_is_pure` rule의 forbidden package에 `lombok..` 포함 |
|
|
| application↔adapter/transport 격리 | `application_does_not_depend_on_adapters_or_transport` |
|
|
| @Transactional ban | `application_does_not_use_spring_transactional_annotation` (FQN `org.springframework.transaction.annotation.Transactional`) |
|
|
| ApplicationContext banned-class (D11) | `application_does_not_depend_on_application_context` (FQN `org.springframework.context.ApplicationContext`) |
|
|
| adapter-adapter 격리 | `web_/persistence_/outbound_adapter_does_not_depend_on_*` 3 rule |
|
|
| web DTO containment | `web_dtos_stay_in_web_adapter` |
|
|
| shared-contract scope | `shared_contract_contains_only_operational_contract_packages` (operational allowlist) |
|
|
| production → sample ban | `production_code_does_not_depend_on_sample_portfolio` |
|
|
| Gradle build-graph 검사 | `src/build.gradle:53` `verifyCleanArchitectureDependencies` + `allowedProjectDependencies` matrix(9 module) |
|
|
| negative fixture | `ArchitectureViolationFixtureTest` + `architecture/violations/...`(SpringDependentDomainFixture·ApplicationContextDependentFixture·TransactionalAnnotatedFixture 등) |
|
|
| D11 한계(string-key bypass) | rule 주석에 명시 — `getBean(Class)`까지만 catch, `getBean(String)`/`Class.forName` 범위 밖 |
|
|
|
|
> ⚠️ ground-truth `CleanArchitectureTest`는 본 slice 이후 boundary-validation / streaming / serialization / resource-identifier / api-contract slice의 rule도 다수 포함한다(현재 30+ rule). 위 표는 본 enforcement-rules slice 소유 항목만 골랐고, 나머지는 각 branch ingest에서 대조한다.
|
|
|
|
### Blog-topic ingest: clean-architecture-module-blueprint (2026-07-02)
|
|
|
|
[[raw/blog-topics/clean-architecture-module-blueprint-2026-05-28]] 는 Clean Architecture skeleton에서 Gradle module boundary를 1차 강제선으로, package 내부 책임 분류를 2차 강제선으로 둔 이유를 블로그로 풀기 위한 raw seed다.
|
|
|
|
- **locally-verified 로 말할 수 있는 부분**: module include, production root, `scanBasePackages`, Gradle dependency matrix, package anchor, sample isolation, enforcement-rules slice 검증 범위.
|
|
- **project-local policy 로 말할 부분**: Spring Modulith를 즉시 도입하지 않고 Gradle + ArchUnit 최소 검증으로 시작한 선택.
|
|
- **블로그 전 과장 방지**: 운영 검증이나 전체 Phase C2 완료처럼 쓰지 않고, module/package blueprint slice의 로컬 검증으로 제한한다.
|
|
- [[raw/blog-topics/executable-clean-architecture-onboarding-2026-06-25]]: Clean Architecture 체크리스트를 README가 아니라 test-only dry-run slice와 negative fixture로 만들어 새 도메인 추가 경계를 CI에서 반복 검증하는 글감. local verification이며 보편 표준 증명처럼 쓰지 않는다.
|
|
- [[raw/blog-topics/clean-architecture-boundary-enforcement-2026-05-28]]: Gradle project-dependency matrix와 ArchUnit bytecode rule을 나눠 Clean Architecture boundary drift를 막는 글감. runtime lookup / MapStruct exemption 같은 planned 항목은 구현 완료로 쓰지 않는다.
|
|
- [[raw/blog-topics/archunit-violations-as-data-pattern-2026-05-28]]: ArchUnit rule의 vacuous pass를 막기 위해 violations-as-data fixture와 negative test로 rule 자체를 검증하는 글감. static analysis 한계를 보완하는 패턴이지 reflection bypass를 해결하는 것은 아니다.
|
|
- [[raw/blog-topics/archunit-generic-return-type-purity-query-port-2026-06-05]]: `List<DomainType>` 같은 generic return type leak을 `getAllInvolvedRawTypes()`로 잡는 query port purity 글감. Object/downcast/reflection 우회까지 잡는다고 쓰지 않는다.
|
|
- [[raw/blog-topics/domain-modeling-guardrails-as-archunit-fitness-functions-2026-06-05]]: DDD marker annotation과 ArchUnit rule로 value object / aggregate / domain event guardrail을 강제하는 글감. marker taxonomy는 ca-tmpl project-local rule로 제한한다.
|
|
|
|
## 관련 개념
|
|
|
|
- [[wiki/concepts/clean-architecture-package-layout]]
|
|
- [[wiki/concepts/archunit-scope-classpath-vs-package-filter]] — `@AnalyzeClasses` 분석 scope(classpath import vs package filter)와 `allowEmptyShould` empty-anchor 함정의 일반 지식
|
|
|
|
## Sources
|
|
|
|
- [[raw/project-notes/ca-skeleton-operational-contract]] (§20 Skeleton Blueprint Contract, §29 Topic 1)
|
|
- [[raw/branch-notes/feature-skeleton-package-blueprint-contract]]
|
|
- [[raw/blog-topics/clean-architecture-module-blueprint-2026-05-28]] — module/package blueprint 블로그 글감 raw seed
|
|
- [[raw/branch-notes/feature-architecture-enforcement-rules]]
|
|
- [[raw/branch-notes/feature-domain-feature-onboarding-contract]]
|
|
- [[raw/blog-topics/executable-clean-architecture-onboarding-2026-06-25]] — executable onboarding guardrails 블로그 글감 raw seed
|
|
- [[raw/blog-topics/clean-architecture-boundary-enforcement-2026-05-28]] — Gradle + ArchUnit boundary enforcement 블로그 글감 raw seed
|
|
- [[raw/blog-topics/archunit-violations-as-data-pattern-2026-05-28]] — violations-as-data ArchUnit fixture 블로그 글감 raw seed
|
|
- [[raw/blog-topics/archunit-generic-return-type-purity-query-port-2026-06-05]] — generic return type purity guardrail 블로그 글감 raw seed
|
|
- [[raw/blog-topics/domain-modeling-guardrails-as-archunit-fitness-functions-2026-06-05]] — domain modeling guardrail 블로그 글감 raw seed
|
|
|
|
## Cluster / 묶음
|
|
|
|
<!-- GENERATED: derived-blogs:start -->
|
|
- [[wiki/blog/ca-tmpl-clean-architecture-package-layout-2026-07-02]]
|
|
<!-- GENERATED: derived-blogs:end -->
|