· Architecture · Auroviq

Clean Architecture in Practice: A Guide for Product Engineering Teams

Practical Clean Architecture for product engineering: dependency inversion, use cases, testing seams, and when modular monoliths beat pure hexagons. From Aur…

Clean Architecture in Practice: A Guide for Product Engineering Teams

Clean Architecture is often taught as a diagram of concentric circles. Product teams need something sharper: a dependency rule they can enforce in CI, a place for business rules that does not depend on frameworks, and enough structure to change databases or UIs without rewriting the company.

This guide is for senior engineers, tech leads, and CTOs who want architecture that ships features—not conference slides. Related reading: microservices vs modular monolith and platform engineering for delivery speed.

The dependency rule in one sentence

Source code dependencies must point inward: outer layers (web, database, message buses, third-party SDKs) may depend on inner layers (domain and application use cases). Inner layers must not know about outer ones.

If your domain package imports Express, NestJS, Spring annotations, or Prisma types, the rule is already broken—regardless of folder names.

Four layers that work for mid-size products

  1. Domain — entities, value objects, domain services, invariants. No HTTP, no SQL, no cloud SDKs.
  2. Application — use cases / application services, ports (interfaces), orchestration, transactions at the use-case boundary.
  3. Adapters (interface) — controllers, GraphQL resolvers, CLI, workers that translate outside world → application commands.
  4. Infrastructure — repositories, email, queues, object storage, AI model clients implementing ports.

Hexagonal Architecture (“ports and adapters”) is the same idea with different vocabulary. Clean Architecture popularized the naming; the win is the boundary discipline.

Folder structure that survives growth

src/
  domain/
    order/
      Order.ts
      OrderId.ts
      Money.ts
  application/
    place-order/
      PlaceOrder.ts
      PlaceOrderPort.ts
  adapters/
    http/
      PlaceOrderController.ts
    persistence/
      OrderRepositoryPrisma.ts
  main/
    compose.ts   # wiring / DI

Prefer packaging by feature or bounded context over packaging only by technical layer once you pass ~10 engineers. Hybrid models work: domain + application per context, shared kernel kept tiny.

Use cases beat “fat controllers” and “god services”

A use case should answer one business question: place order, refund payment, invite member. It:

Keep controllers dumb. Keep domain free of framework lifecycle hooks.

When Clean Architecture is overkill

Architecture is a product investment. Optimize for the next 12–24 months of change, not imaginary infinite scale.

Testing seams you get for free

With ports, you can unit-test use cases with in-memory repositories and fake clocks. Save expensive E2E for critical paths. Contract tests protect adapter boundaries when you extract a service later—see our companion piece on testing strategy.

Enforcement checklist

Frequently asked questions

Is Clean Architecture the same as DDD?

No. Clean Architecture organizes dependencies. Domain-Driven Design models the business. They pair well but solve different problems.

Do we need a full hexagonal setup on day one?

No. Start with domain + application modules and strict import rules. Add ports when you need multiple adapters or heavy testing isolation.

How can Auroviq help?

We audit codebases, introduce enforceable module boundaries, and coach teams while shipping product—architecture as delivery, not a rewrite holiday.

Work with Auroviq

Auroviq (AuroviQ) is a custom software and AI engineering agency based in Ahmedabad and Bhubaneswar, India, serving product companies in the UK, Netherlands, Singapore, and the US. We design architectures, ship production systems, and embed dedicated engineering teams.

Work with Auroviq — custom software & AI for product teams.

Contact us · More articles · Services