Architectural · App Structure · DWG NO. 046

Clean Architecture

Arrange the codebase in concentric rings with dependencies always pointing inward, so the domain at the center never depends on outer detail.

TypeApplication Structure
ScopeWhole application
ComplexityMedium–High
Common inLong-lived domain-rich systems, systems expecting to outlive their frameworks

Layered architecture still lets the domain depend on infrastructure

In a typical layered setup, business logic often directly imports data-access classes — meaning the domain's compile-time dependency graph points outward, toward infrastructure. If the entities that matter most to the business are the ones tied most tightly to a specific database or framework, they're also the hardest things to change safely.

Entities at the center; dependencies point in, never out

Entities (core business rules) sit at the center, wrapped by use cases, wrapped by interface adapters, wrapped by frameworks and drivers at the outer edge. The strict rule: source code dependencies can only point inward. An outer ring can know about an inner one; an inner ring never imports anything from an outer one.

EntitiesUse CasesInterface\nAdaptersdeps point inward

An entity with zero outward dependencies

`Order` (the entity) knows nothing about how it gets saved. The use case coordinates it with a port; only the outer adapter knows about the real database.

clean-architecture-order.js
// Entity — pure business rule, imports nothing outer.
class Order {
  #items;
  constructor(items) {
    if (items.length === 0) throw new Error("order must have items");
    this.#items = items;
  }
  get total() { return this.#items.length * 10; }
}

// Use case — coordinates entities via a port, still no infrastructure import.
function placeOrderUseCase(orderRepoPort, items) {
  const order = new Order(items);
  orderRepoPort.save(order);
  return order;
}

// Outer ring: interface adapter + framework/driver detail.
const postgresOrderRepo = { save: (order) => console.log(`db: saved order, total ${order.total}`) };

placeOrderUseCase(postgresOrderRepo, ["book", "pen"]);
// Order (entity) and placeOrderUseCase have no idea Postgres exists.

Trade-offs