chore: remove sample portfolio module
This commit is contained in:
@@ -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 운영 설정을 함께 명시해야 한다.
|
||||
|
||||
|
||||
@@ -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 형제
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 캐시 정책.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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..`).
|
||||
|
||||
+1
-2
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user