Architecture overview

An overview of a layered, multi-repository architecture designed to separate concerns, ease evolution, and scale with greater control.

Goal of this architecture

The proposed architecture aims to solve a common problem in systems that grow:

  • too many responsibilities mixed together
  • difficulty deploying without risk
  • external integrations polluting the domain
  • growing complexity in both frontend and backend
  • coupling between teams and components

To address this, the system is organized into layers and services with clearer boundaries.

The idea is not to add complexity for its own sake, but to distribute complexity to places where it is more manageable.

The 7 layers

1. Presentation

This is where the user interface lives. It renders screens, manages state, validates forms on the client side, and consumes APIs.

2. API Gateway

This is the main entry point. It handles authentication, routing, rate limiting, logging, and other cross-cutting concerns.

3. BFF

It aggregates and transforms data for the UI. It reduces frontend complexity and prevents the client from knowing too many internal details.

4. Microservices

They contain business logic organized by domains or bounded contexts. This is where the rules, use cases, domain events, and persistence live.

5. Event Bus

It enables asynchronous communication between services through events. It promotes temporal decoupling and the construction of distributed processes.

6. ACL

It protects the domain from external or legacy systems. It translates models and encapsulates adapters so the business core stays clean.

7. Data / External Systems

This includes databases, caches, legacy systems, and third-party providers.

Why separate into layers?

Separating into layers allows for:

Clarity of responsibilities

Each part of the system has a more specific focus.

Lower coupling

The frontend doesn’t need to know the internal details of each microservice. Domain services shouldn’t depend directly on the model of a legacy ERP.

Technical scalability

You can scale different components according to their real needs.

Organizational scalability

Teams can work on clearer areas, with well-defined ownership.

Controlled evolution

Changing one piece has less impact on the others when boundaries are well defined.

Communication between layers

In this architecture, two major forms of communication coexist:

Synchronous communication

It is used when a layer needs an immediate response. For example:

  • frontend → gateway
  • gateway → BFF
  • BFF → microservices

It usually happens over HTTP or GraphQL/REST.

Asynchronous communication

It is used when a service publishes a fact and others react without needing to respond immediately.

For example:

  • ms-orders publishes OrderPlaced
  • inventory reacts
  • notifications react
  • analytics reacts

Both coexist because they solve different needs.

Multi-repo: why separate repositories

The architecture is designed around multiple repositories, one per layer or per main service.

This allows for:

  • deployment independence
  • clearer ownership
  • smaller size per repository
  • better isolation by domain

But it also requires:

  • shared standards
  • versioning agreements
  • discipline with contracts
  • control over the shared kernel

Multi-repo is not automatically better; it is a decision that should accompany a real need for separation.

Principles that support the architecture

Separation of concerns

Each layer solves a different problem.

Protected domain

Business rules must live in the right place and not be polluted by external models.

Explicit contracts

Communication between parts of the system must be defined and understandable.

Independent evolution

Each part should be able to change with as little friction as possible.

Observability and security by design

They shouldn’t be added at the end as a patch.

What this architecture does not mean

It’s important to avoid some misinterpretations.

It doesn’t mean everything must be a microservice

Separate when there is a clear boundary and a real need.

It doesn’t mean more layers is always better

Each layer must justify the value it provides.

It doesn’t mean complexity disappears

Complexity doesn’t disappear; it is organized better.

It doesn’t mean the shared kernel can grow without control

Sharing too much can recreate strong coupling.

How to work through this section

We recommend moving forward like this:

  1. Frontend
  2. API Gateway
  3. BFF
  4. Microservices
  5. Event Bus
  6. ACL
  7. Data / External Systems
  8. Shared Kernel and contracts
  9. Repositories

Summary

This architecture organizes the system into layers with distinct responsibilities to promote clarity, scalability, and controlled evolution. It doesn’t seek to impose complexity, but to place each type of complexity where it can be handled best.