One Entry Point per Module for Business Logic
Goal
Every business-logic entry point must be a separate module. Do not group multiple entry points together in the same class, object, or file because they operate on the same domain entity type.
A module here means the unit of code organization provided by the language: a file, a single-class file, a single-object file, or equivalent. Each entry point gets its own.
What Counts as In Scope
Apply this skill to code that does one or more of these things:
- defines multiple business-logic entry points in a single class, object, or file
- groups command handlers, query handlers, use cases, or application services by domain entity type into a shared container
- introduces a service class or facade that bundles several business operations as methods on one object
- organizes business-logic entry points by entity rather than by operation
The Rule
Each business-logic entry point must live in its own module.
- One command handler per module.
- One query handler per module.
- One use case per module.
- One application service method becomes one application service module with a single public function.
Do not group entry points by domain entity type.
- Do not create a
UserService class with createUser, updateUser, findUserById, and deleteUserById as methods.
- Do not create a
ReservationUseCases class that bundles all reservation-related operations.
- Do not create a file that exports multiple entry-point functions for the same entity.
Each module exposes exactly one business-logic entry point.
- If the entry point is a top-level function, the module contains that function as its single public entry point.
- If the entry point is a class or object, it must have exactly one public function that acts as the entry point. Construction, dependency injection, and private helpers do not count as additional public entry points.
Name the module after the entry point.
- The file or module name should reflect the specific business operation, not the domain entity type.
- Prefer names like
create-reservation-command-handler, find-user-by-id-query-handler, or the local equivalent over names like user-service, reservation-use-cases, or order-handlers.
Why Not Group by Entity
Grouping entry points by domain entity creates classes or files that grow with every new operation. This leads to:
- large files with unrelated operations sharing scope and dependencies
- entry points that accumulate shared private state or helpers that blur their boundaries
- difficulty isolating one operation's dependencies from another's
- merging conflicts when different developers work on different operations for the same entity
Keeping each entry point in its own module makes each operation self-contained, with its own explicit dependencies and no accidental coupling to sibling operations.
Detection Workflow
Find classes, objects, or files that contain multiple business-logic entry points.
- Look for service classes, use-case bundles, handler collections, or files that export several entry-point functions.
- Check whether the grouping criterion is the domain entity type.
Count the public entry points per module.
- If a class or object has more than one public function that acts as a business-logic entry point, it violates this rule.
- If a file exports more than one entry-point function, it violates this rule.
Check the module name.
- If the module is named after a domain entity type rather than a specific operation, it likely groups multiple entry points.
Writing or Changing Entry Points
Create a new module for each new entry point.
- Do not add a new method to an existing entity-grouped class.
- Create a new file with the entry point as its single public operation.
When refactoring, extract grouped entry points into separate modules.
- Move each method from an entity-grouped class into its own module.
- Give each module its own explicit dependencies.
Keep private helpers local to the entry point that uses them.
- If a helper is used by only one entry point, keep it in that entry point's module.
- If a helper is shared across multiple entry points, extract it into its own module rather than keeping it as a shared private method in a grouped class.
Examples
Avoid this:
// user-service.ts
class UserService {
createUser(command: CreateUserCommand): Promise<CreateUserCommandHandlerSuccess> { ... }
updateUser(command: UpdateUserCommand): Promise<void> { ... }
findUserById(query: FindUserByIdQuery): Promise<FindUserByIdQueryHandlerSuccess> { ... }
deleteUserById(command: DeleteUserByIdCommand): Promise<void> { ... }
}
Prefer this:
create-user-command-handler.ts
update-user-command-handler.ts
find-user-by-id-query-handler.ts
delete-user-by-id-command-handler.ts
Each file contains exactly one entry point.
Review Questions
When reading or reviewing code, ask:
- Does this module contain more than one business-logic entry point?
- Are multiple entry points grouped because they operate on the same domain entity type?
- Does the module name refer to a domain entity type rather than a specific business operation?
- Would adding a new operation for this entity require modifying this module?
If the answer is yes, apply this skill.
Report the Outcome
When finishing the task:
- state which entry points were identified or separated into their own modules
- state which entity-grouped classes or files were split
- state how each module was named after its specific business operation
1---2name: business-logic-entry-point-one-per-module3description: Require each business-logic entry point to live in its own separate module. Use when an agent needs to create, modify, review, or organize business-logic entry points such as command handlers, query handlers, use cases, or application services. Do not group multiple entry points in a single class, object, or file based on the domain entity they operate on. Each entry point must be either a top-level function in its own module, or a class or object with exactly one public function in its own module.4---56# One Entry Point per Module for Business Logic78## Goal910Every business-logic entry point must be a separate module. Do not group multiple entry points together in the same class, object, or file because they operate on the same domain entity type.1112A module here means the unit of code organization provided by the language: a file, a single-class file, a single-object file, or equivalent. Each entry point gets its own.1314## What Counts as In Scope1516Apply this skill to code that does one or more of these things:1718- defines multiple business-logic entry points in a single class, object, or file19- groups command handlers, query handlers, use cases, or application services by domain entity type into a shared container20- introduces a service class or facade that bundles several business operations as methods on one object21- organizes business-logic entry points by entity rather than by operation2223## The Rule24251. Each business-logic entry point must live in its own module.26 - One command handler per module.27 - One query handler per module.28 - One use case per module.29 - One application service method becomes one application service module with a single public function.30312. Do not group entry points by domain entity type.32 - Do not create a `UserService` class with `createUser`, `updateUser`, `findUserById`, and `deleteUserById` as methods.33 - Do not create a `ReservationUseCases` class that bundles all reservation-related operations.34 - Do not create a file that exports multiple entry-point functions for the same entity.35363. Each module exposes exactly one business-logic entry point.37 - If the entry point is a top-level function, the module contains that function as its single public entry point.38 - If the entry point is a class or object, it must have exactly one public function that acts as the entry point. Construction, dependency injection, and private helpers do not count as additional public entry points.39404. Name the module after the entry point.41 - The file or module name should reflect the specific business operation, not the domain entity type.42 - Prefer names like `create-reservation-command-handler`, `find-user-by-id-query-handler`, or the local equivalent over names like `user-service`, `reservation-use-cases`, or `order-handlers`.4344## Why Not Group by Entity4546Grouping entry points by domain entity creates classes or files that grow with every new operation. This leads to:4748- large files with unrelated operations sharing scope and dependencies49- entry points that accumulate shared private state or helpers that blur their boundaries50- difficulty isolating one operation's dependencies from another's51- merging conflicts when different developers work on different operations for the same entity5253Keeping each entry point in its own module makes each operation self-contained, with its own explicit dependencies and no accidental coupling to sibling operations.5455## Detection Workflow56571. Find classes, objects, or files that contain multiple business-logic entry points.58 - Look for service classes, use-case bundles, handler collections, or files that export several entry-point functions.59 - Check whether the grouping criterion is the domain entity type.60612. Count the public entry points per module.62 - If a class or object has more than one public function that acts as a business-logic entry point, it violates this rule.63 - If a file exports more than one entry-point function, it violates this rule.64653. Check the module name.66 - If the module is named after a domain entity type rather than a specific operation, it likely groups multiple entry points.6768## Writing or Changing Entry Points69701. Create a new module for each new entry point.71 - Do not add a new method to an existing entity-grouped class.72 - Create a new file with the entry point as its single public operation.73742. When refactoring, extract grouped entry points into separate modules.75 - Move each method from an entity-grouped class into its own module.76 - Give each module its own explicit dependencies.77783. Keep private helpers local to the entry point that uses them.79 - If a helper is used by only one entry point, keep it in that entry point's module.80 - If a helper is shared across multiple entry points, extract it into its own module rather than keeping it as a shared private method in a grouped class.8182## Examples8384Avoid this:8586```ts87// user-service.ts88class UserService {89 createUser(command: CreateUserCommand): Promise<CreateUserCommandHandlerSuccess> { ... }90 updateUser(command: UpdateUserCommand): Promise<void> { ... }91 findUserById(query: FindUserByIdQuery): Promise<FindUserByIdQueryHandlerSuccess> { ... }92 deleteUserById(command: DeleteUserByIdCommand): Promise<void> { ... }93}94```9596Prefer this:9798```99create-user-command-handler.ts100update-user-command-handler.ts101find-user-by-id-query-handler.ts102delete-user-by-id-command-handler.ts103```104105Each file contains exactly one entry point.106107## Review Questions108109When reading or reviewing code, ask:110111- Does this module contain more than one business-logic entry point?112- Are multiple entry points grouped because they operate on the same domain entity type?113- Does the module name refer to a domain entity type rather than a specific business operation?114- Would adding a new operation for this entity require modifying this module?115116If the answer is yes, apply this skill.117118## Report the Outcome119120When finishing the task:121122- state which entry points were identified or separated into their own modules123- state which entity-grouped classes or files were split124- state how each module was named after its specific business operation