shaikapsar/arch-design-rules icon
public
Published on 5/8/2025
Architecture and Design Rules

Rules

Repository Placement Rule

  • Define repository interfaces (e.g., OrderRepository) in the domain layer as output ports.
  • Repository implementations reside in the infrastructure layer and use persistence technology (e.g., JPA, MongoDB).
  • Repositories must deal only with aggregate roots, not child entities or value objects.
  • Application services use repositories to load and persist aggregates.
  • Avoid business logic in repositories; focus on data access.
  • Ensure repository interfaces are technology-agnostic.
  • Prevent direct repository access from domain entities to preserve encapsulation.

Input Port Rule

  • Input ports represent use cases and are defined as interfaces in the application layer.
  • Input ports are invoked by controllers, APIs, or UI layers.
  • Application services implement input ports and coordinate domain logic and infrastructure.

Output Port Rule

  • Output ports represent dependencies on external systems (e.g., repositories, message brokers).
  • Place domain-relevant output ports (e.g., repositories) in the domain layer.
  • Place application-level output ports (e.g., email, payment) in the application layer.
  • Implement output ports in the infrastructure layer with technology-specific adapters.
  • Keep output port interfaces clean, focusing on business capabilities rather than technology concerns.

Hexagonal Architecture Layers

  • Domain Layer: Contains core domain models, aggregates, entities, value objects, domain events, and domain-level output ports.
  • Application Layer: Coordinates use cases, defines input ports, and uses domain services and output ports.
  • Infrastructure Layer: Implements technical concerns like database access, messaging, external APIs.
  • Presentation Layer (optional): Interfaces with the user, typically via HTTP or WebSocket.
  • Apply Dependency Inversion: inner layers define interfaces, outer layers implement them.
  • Maintain strict boundaries and avoid circular dependencies.

Domain-Driven Design (DDD) Layering

  • Follow the DDD principles: model around the business domain.
  • Use Aggregates to enforce invariants and model transactional consistency boundaries.
  • Use Value Objects to model concepts without identity.
  • Use Entities to model stateful business concepts with identity.
  • Encapsulate domain rules and logic inside aggregates.
  • Use Domain Services only when logic doesn’t naturally belong to an entity or value object.
  • Place shared domain logic and factories in the domain layer.
  • Align boundaries using Bounded Contexts — model consistency boundaries for different subdomains.

CQRS Rule

  • Separate read and write operations using different models and components.
  • Commands mutate state and are handled by command-side services.
  • Queries retrieve data, often optimized for read performance (e.g., projections).
  • Avoid using domain aggregates in the query layer.
  • Apply CQRS only where it simplifies complexity or enables scalability.

SAGA Coordination Rule

  • Use SAGA patterns to coordinate multi-step, long-running processes across bounded contexts or services.
  • Orchestrated SAGA: central orchestrator controls flow, often implemented in application layer.
  • Choreographed SAGA: distributed coordination through domain events, no central controller.
  • Each step should be idempotent and compensatable.
  • Model each SAGA as a state machine or flow.
  • Persist SAGA state to support recovery and retry.

Event Sourcing Rule

  • Persist domain events instead of current state snapshots.
  • Rehydrate aggregates by replaying their event stream.
  • Events represent historical facts (e.g., OrderPlaced, PaymentCaptured).
  • Commands result in new events; events are then applied to the aggregate.
  • Use snapshots for performance if event streams are long.
  • Apply versioning and schema evolution to support long-lived events.

Outbox Pattern Rule

  • Persist events in an outbox table as part of the same transaction that changes domain state.
  • Use an outbox relay to publish events to external systems (e.g., Kafka, RabbitMQ).
  • Decouple business logic from messaging concerns using this pattern.
  • Ensure idempotent publishing using message IDs or deduplication strategies.
  • Use this pattern to achieve transactional consistency across database and message queues.

General Architecture Guidelines

  • Ensure domain logic is the heart of the system and is isolated from infrastructure.
  • Adhere to clean architecture: direction of dependencies points inward.
  • Use dependency injection to bind interface contracts to implementations.
  • Avoid anemic domain models — embed behavior within aggregates.
  • Use rich domain models to reflect business understanding and terminology.
  • Structure your codebase by feature or subdomain, not technical layer.
  • Apply DDD strategic design to split domains into meaningful bounded contexts.
  • Integrate between contexts via APIs, events, or shared kernel — be explicit.
  • Evolve architecture incrementally — not all systems need all patterns upfront.