chore: remove sample portfolio module

This commit is contained in:
DongHyeonka
2026-08-13 21:47:15 +09:00
parent e552e317d6
commit 697fc740e6
321 changed files with 190 additions and 18961 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ Package root: `dev.caskeleton.adapter.inbound.graphql`.
자동 합성/바인딩하도록 얹는 얇은 계층이다.
- feature-agnostic: `classpath:graphql/**` 스키마와 모든 `@Controller` `@QueryMapping`/
`@MutationMapping` 을 generic 하게 합성한다. **WorkLog 등 구체 기능을 이름으로 알지 않는다.**
- classpath opt-in: 현재 `app-bootstrap`/`sample-portfolio` production runtime 은 이 leaf 를
- classpath opt-in: 현재 `app-bootstrap` production runtime 은 이 leaf 를
의존하지 않는다. 실제 채택 시 composition root 가 GraphQL leaf 와 인증/인가·CORS 정책,
GraphiQL/introspection 운영 설정을 함께 명시해야 한다.
+5 -6
View File
@@ -19,18 +19,17 @@ Spring for GraphQL 은 schema-first 다. 빈 스키마로는 부팅이 실패하
## 기능(feature)은 어떻게 기여하는가 — machinery/feature 분리
스켈레톤은 **WorkLog 를 이름으로 알지 못한다.** 향후 composition root 가 이 모듈을 classpath 에
스켈레톤은 구체적인 제품 기능을 이름으로 알지 못한다. 향후 composition root 가 이 모듈을 classpath 에
명시적으로 채택하고 feature 를 추가하면 Spring for GraphQL 이 다음 두 축으로 합성할 수 있다:
- **스키마**: `classpath:graphql/**/*.graphqls` 를 전부 병합한다. sample 모듈의
향후 `worklog.graphqls` 같은 feature 스키마는 스켈레톤의 `skeleton.graphqls` 와 합쳐진다.
- **스키마**: `classpath:graphql/**/*.graphqls` 를 전부 병합한다. 제품 모듈의 feature 스키마는
스켈레톤의 `skeleton.graphqls` 와 합쳐진다.
- **resolver(핸들러)**: 컨텍스트의 모든 `@Controller``@QueryMapping`/`@MutationMapping`
메서드를 바인딩한다. 향후 feature 의 GraphQL controller 는 스켈레톤을 수정하지 않고 등록할 수
있다.
현재 `app-bootstrap``sample-portfolio` production runtime 은 이 leaf 를 의존하지 않는다.
즉 이 모듈은 **classpath opt-in**며, 현재 sample 에 feature GraphQL 스키마/controller 가 있다
뜻이 아니다. leaf 자체는 최소 health 스키마로 독립 기동할 수 있다.
현재 `app-bootstrap` production runtime 은 이 leaf 를 의존하지 않는다. 즉 이 모듈은
**classpath opt-in**다. leaf 자체는 최소 health 스키마로 독립 기동할 수 있다.
## 에러 매핑 — web `GlobalExceptionHandler` / gRPC 인터셉터의 GraphQL 형제
+5 -8
View File
@@ -58,8 +58,8 @@ also drives the runtime patterns reference implementations must follow.
`Optional<T>` wrappers on request records.
- **B3 — Mapper-internal failures.** Map record canonical-constructor
`IllegalArgumentException`, MapStruct generated NPE, ACL normalization
failures, etc. by throwing `MappingException` (sample implementation in
`sample-portfolio`); the global handler routes it to `MAPPING_FAILED` (HTTP 400),
failures, etc. by throwing `MappingException`; the global handler routes it to
`MAPPING_FAILED` (HTTP 400),
never to `BAD_PARAMETER` or `INTERNAL_ERROR`. Plain `IllegalArgumentException`
remains `BAD_PARAMETER` for non-mapper callers.
- **B4 — Validation layering.** Class-level Bean Validation constraints belong
@@ -93,16 +93,13 @@ Domain `@RestControllerAdvice` in a consuming module must be annotated
`@Order(Ordered.HIGHEST_PRECEDENCE)` (or otherwise ordered ahead of this
module's base `GlobalExceptionHandler`), because the base handler's catch-all
`@ExceptionHandler(Exception.class)` would otherwise resolve domain exceptions
to `INTERNAL_ERROR`. See `sample-portfolio`'s `DomainExceptionHandler` for the
pattern.
to `INTERNAL_ERROR`.
The base operational handler (`error/GlobalExceptionHandler`), the error-code
contract (`dev.caskeleton.shared.error.ApiErrorCode` + `OperationalError`), the
`error/ErrorResponseFactory`, and the `envelope/EnvelopeBodyAdvice` now live in
production modules (`adapter:inbound:web` / `shared-contract`), so the running application
provides them without depending on `sample-portfolio`. Domain-specific exception
handlers and error codes live in the consuming module (see sample's
`DomainExceptionHandler` / `PortfolioErrorCode`).
production modules (`adapter:inbound:web` / `shared-contract`). Domain-specific exception
handlers and error codes live in the consuming module.
## Schema / serialization contract
+1 -2
View File
@@ -225,8 +225,7 @@ commit-aware response wrapper 계약을 구현한다. 또한 이 모듈은 HTML
### EnvelopeBodyAdvice
- 컨트롤러는 도메인/DTO 타입을 반환하고, 이 advice 가 와이어 형태를 항상
`{success, data | error, traceId}` 로 보장한다.
- 위치: sample 모듈이 아니라 adapter-web. 실행 앱은 adapter-web 에 의존하지만 sample-portfolio 에는 의존하지
않으므로, 응답 래핑이 실제로 동작하려면 여기 있어야 한다.
- 위치: adapter-web. 실행 앱이 이 모듈을 의존하므로 응답 래핑이 실제 runtime에 적용된다.
### CacheControlFilter
- 스켈레톤 기본 HTTP 캐시 정책.
+1 -1
View File
@@ -66,7 +66,7 @@ The broadcaster sends only when exactly one projector matches. Zero or duplicate
projection failures, invalid event types, null fields, and over-limit maps are dropped. The wire
envelope contains only `type`, bounded string `fields`, and `occurredAt`.
No current `sample-portfolio` feature publishes a WebSocket event or contributes a projector.
No current production feature publishes a WebSocket event or contributes a projector.
That is a future adoption example, not an existing runtime feature.
## Evidence and limits
+2 -2
View File
@@ -5,8 +5,8 @@ live in [CLAUDE.md](CLAUDE.md); this document records why the P1 boundary has th
## Opt-in instead of accidental exposure
The leaf is built and tested but is not part of `app-bootstrap` or `sample-portfolio` production
runtime membership. Even after a future composition adds it, `ca-skeleton.websocket.enabled=false`
The leaf is built and tested but is not part of `app-bootstrap` production runtime membership.
Even after a future composition adds it, `ca-skeleton.websocket.enabled=false`
keeps its configuration and broadcaster absent. Enabling requires explicit non-wildcard origins,
so merely adding the artifact cannot expose a wildcard STOMP broker.
+1 -1
View File
@@ -42,7 +42,7 @@ Package root: `dev.caskeleton.adapter.outbound.fileserver`. Driven (outbound) ad
## Forbidden
- Inbound adapters, sibling outbound adapters, persistence, `app-bootstrap`, `sample-portfolio`
- Inbound adapters, sibling outbound adapters, persistence, or `app-bootstrap`
(ArchUnit `OUTBOUND_ADAPTERS_*` family rules).
- Leaking filesystem, stream, framework, or provider types across `FilePublicationPort`.
- Advertising `FILE_AND_DIRECTORY_SYNC` as physical device/controller/replica/site power-loss
+1 -2
View File
@@ -37,8 +37,7 @@ build choices) live in [README.md](README.md).
- Persistence or web technology (JPA/Hibernate/Spring Data/Spring Web) — ArchUnit
`identifier_adapter_does_not_depend_on_other_adapters_or_bootstrap`;
`.claude/hooks/ca_import_gate.py` G4 가 쓰기 시점에 차단.
- inbound adapters, persistence adapters, other outbound leaves, `app-bootstrap`,
`sample-portfolio`.
- inbound adapters, persistence adapters, other outbound leaves, or `app-bootstrap`.
- External IO (HTTP / messaging / cache / DB) — that belongs in `adapter-outbound`.
## Test
+1 -1
View File
@@ -34,7 +34,7 @@ readiness live in [README.md](README.md) and
## Forbidden
- Inbound adapters, sibling outbound adapters, persistence, app-bootstrap, or sample-portfolio
- Inbound adapters, sibling outbound adapters, persistence, or app-bootstrap
dependencies.
- Provider keys, paths, locators, SDK types, Spring types, or control-record types leaking into
application-core.
@@ -68,10 +68,9 @@ Adapters that serve one optional capability carry that capability's switch, unli
module. The `fileserver` package is the current case: every `Jpa*` adapter there is annotated
`@ConditionalOnProperty(prefix = "app.fileserver-platform", name = "enabled", havingValue = "true")`.
Without the gate a composition root that merely includes this module builds those beans, and each of
them needs collaborators only the Fileserver configuration supplies — which is how `sample-portfolio`
came to fail on a `FileStateMachine` it has no use for. A store for a capability nobody enabled
should not exist.
Without the gate a composition root that merely includes this module builds those beans, although
their collaborators exist only when Fileserver configuration is selected. A store for a capability
nobody enabled should not exist.
## Allowed
@@ -174,7 +173,7 @@ framework-neutral `shared.error.PersistenceFailureException` carrying one of the
Audit metadata (`created_at` / `updated_at` / `created_by` / `updated_by`, D3) is an
infrastructure concern that must never reach `domain-core` (D2). It lives only on the
`audit/AuditableEntity` `@MappedSuperclass`; a domain aggregate persistence entity opts in
by extending it (D6 — e.g. the sample `WorkLogEntity`). The domain aggregate itself carries
by extending it (D6). The domain aggregate itself carries
zero audit fields, enforced by ArchUnit `domain_is_pure` (no `jakarta.persistence..`) plus
`domain_entities_do_not_carry_audit_fields` (no `createdAt`/`updatedAt`/`createdBy`/`updatedBy`
fields under `..domain..`).
@@ -11,8 +11,7 @@ import org.junit.jupiter.api.Test;
* (feature-persistence-auditing-contract D1/D3/D4/D5).
*
* <p>Exercised through a tiny concrete subclass because {@link AuditableEntity} is a
* {@code @MappedSuperclass} (JPA cannot instantiate it standalone, and the production subclass
* {@code WorkLogEntity} lives in the sample module).
* {@code @MappedSuperclass} (JPA cannot instantiate it standalone).
*/
class AuditableEntityTest {
@@ -32,7 +32,7 @@ Data MongoDB infrastructure. Design rationale lives in [README.md](README.md).
## Forbidden
- Inbound adapters, sibling outbound adapters, `app-bootstrap`, `sample-portfolio` (ArchUnit
- Inbound adapters, sibling outbound adapters, or `app-bootstrap` (ArchUnit
`OUTBOUND_ADAPTERS_*` family rules).
- Shipping placeholder `Example*` document, repository, record, or adapter types in production.
- Adding idempotency/outbox/lock on Mongo without a separately approved contract.