PackagesInternal architecture

Internal architecture

Every @withpotter/* package follows the same layered (clean-architecture) shape. Once you know the layers, you can read any package — the module reference lists the concrete classes per package.

The layers

LayerFile suffixResponsibility
Controller*.controller.tsDeclares HTTP routes, validates input via DTOs, delegates to a use-case. No business logic.
Use-case*.usecase.tsOne application operation with a single execute() method. Orchestrates repositories, ports, and domain rules.
Repository*.repo.tsData access for one aggregate. Wraps a Sequelize entity; the only layer that builds queries.
Entity*.entity.tsA sequelize-typescript model — the table shape, columns, and relations.
DTO*.dto.tsRequest/response shapes with class-validator decorators; what the global ValidationPipe enforces.
Port*.port.tsA DI token + contract for a capability the package does not own (mail, payments, etc.).
Provider / Adapter*.provider.ts, *.adapter.tsConcrete implementations. The engine binds adapters to ports.
Service*.service.tsShared helpers used by use-cases (pricing, formatting, integrations).
Module*.module.tsThe forRoot() factory that wires the above into a DynamicModule.

How a request flows

The controller is intentionally thin: it maps an HTTP route to exactly one use-case. All logic lives in the use-case, which is why the module reference lists use-cases as the real description of what a package does.

Why this shape

  • Testable. Use-cases are plain classes with one method; they can be unit tested without HTTP or a database.
  • Swappable infrastructure. Cross-package needs go through ports, so a mailer or payment gateway is replaced without touching a use-case.
  • Self-owned data. Each package owns its entities and the only repository that touches them — no package reaches into another’s tables.
  • Composable. The forRoot() module is the single seam the engine wires, per the package contract.

Want the concrete classes? The module reference is generated from source and lists every use-case (with its execute() signature), entity (with fields), and repository (with methods) for each package.