User-Goal Level Operations
A product team writes about what the system does for the user. Subscribe a customer to a plan. Cancel a subscription. Issue a refund. Those statements are use cases in Cockburn's sense and entry-point operations in Uncle Bob's Clean Architecture. @UseCase from @frenchexdev/ddd-use-case reifies them so the same names appear in the PRD, in the compliance report, and in the codebase.
What @UseCase Reifies
The use-case pattern's value is naming alignment. The product team says subscribe customer to plan; the developer team names the class SubscribeCustomerToPlan; the application service exposes a method also named subscribeCustomerToPlan; the command handler dispatches to it; the audit trail records the use-case name. One name, four places, no translation cost.
The pattern is shallower than the application service in two ways. A use case is the name of an entry-point operation, not its implementation. The application service still does the orchestration; the use case is the named token attached to the orchestration. A use case has no internal structure. It is metadata over a method, not a separate class with internal logic. The implementation lives in the application service the use case is attached to.
The discipline is around alignment. Every command handler routes to exactly one use case. Every use case appears in the product requirements. Every requirement that names a use case corresponds to one in the codebase. The cross-referencing is what makes a project's compliance report — which requirements does this build satisfy — answerable mechanically.
The Runtime: ddd-use-case
The runtime is at the M4/M5 stub milestone. The decorator surface will declare the use case's name and the requirement(s) it satisfies; the analyzer will cross-validate the names against the product-requirements registry; the codegen will emit a use-case registry that the compliance machinery consumes.
A sketched future shape:
// Sketch — the runtime decorator is not yet exported.
@UseCase({
name: 'SubscribeCustomerToPlan',
satisfies: ['REQ-SUB-001', 'REQ-BILL-014'],
})
@ApplicationService({ name: 'SubscriptionService' })
export class SubscriptionService {
async subscribeCustomerToPlan(cmd: SubscribeCustomerToPlanCommand): Promise<Result<void, SubscriptionFailure>> {
/* ... orchestration ... */
}
}// Sketch — the runtime decorator is not yet exported.
@UseCase({
name: 'SubscribeCustomerToPlan',
satisfies: ['REQ-SUB-001', 'REQ-BILL-014'],
})
@ApplicationService({ name: 'SubscriptionService' })
export class SubscriptionService {
async subscribeCustomerToPlan(cmd: SubscribeCustomerToPlanCommand): Promise<Result<void, SubscriptionFailure>> {
/* ... orchestration ... */
}
}The use case names the operation and the requirements it serves; the application service supplies the body. A compliance report can then ask: for every requirement REQ-NNN, is there a use case satisfying it, and is that use case covered by a test?
Cross-Links
- Composed onto
@ApplicationServicemethods — the application service supplies the orchestration body, the use case supplies the name. - Routed to by
@CommandHandler— each command handler invokes one use case. - Recorded in
@AuditTrailentries — the use case name is the what in the audit log. - Cross-references the requirements registry — the compliance machinery validates that requirements have matching use cases.
Back to the series index.