Skip to content

Architecture

Patterns

  • Repository Pattern -- Abstract repository interfaces are defined in each service module (e.g. invoicing/repository.py). Concrete per-table implementations live in repository/ (e.g. repository/account.py, repository/vendor.py). Service-facing composites in repository/ (e.g. repository/invoicing.py) combine the per-table repositories via multiple inheritance.
  • Domain-Driven Design -- Business logic separated in domain/ and service/.
  • Controller-Service-Repository -- Layered architecture pattern.

Rules

  • All business logic, including things such as taking the current time and ID generation, must occur in the service layer.
  • Each controller method should call at most one service method. If a handler needs data from multiple operations, create a composite service method that orchestrates them and returns everything the controller needs.
  • In controller path parameters, use domain ID types (e.g. OrganisationId, AccountId) directly instead of raw UUID. Litestar resolves them correctly, and they can be passed straight to service methods and templates without conversion.
  • Define custom exception classes in an appropriate hierarchy for each new type of error. All relevant data should be stored on the exception as attributes.