DEV Community

Francesco Portus
Francesco Portus

Posted on

SmartOrder — Part 2: The Services Layer Architecture

In the previous article, I introduced SmartOrder as a modern microservices reference platform designed around DDD, REST + HATEOAS, OpenAPI, AsyncAPI, and event-driven principles.

In this article, we focus exclusively on the services layer structure of the repository:

👉 https://github.com/portus84/smartorder-ms/tree/develop/services

This is not a deep dive into individual services — those will be covered in dedicated articles.

Instead, this post explains the architectural blueprint behind the services module and why it is structured this way.


Why a Single services Aggregator Module?

Unlike many microservices repositories that split each service into completely separate repositories, SmartOrder adopts a single multi-module Maven structure.

This decision was intentional and driven by:

  • Blueprint visibility
  • Consistent dependency management
  • Architectural governance
  • Easier local development
  • Clear separation between shared infrastructure and business services

The services directory acts as a bounded aggregation root for all deployable microservices.

It is not just a folder — it represents the runtime boundary of the platform.


High-Level Structure

The services directory contains:

  • Multiple Spring Boot microservices
  • Each service as an independent Maven module
  • A shared parent for dependency alignment
  • Strict separation between business logic and infrastructure

Conceptually:

services/
├── service-a
├── service-b
├── service-c
└── pom.xml
Enter fullscreen mode Exit fullscreen mode

Each service:

  • Is independently deployable
  • Has its own Spring Boot entry point
  • Owns its own domain
  • Exposes REST APIs
  • Optionally publishes/subscribes to events

Yet they share:

  • Platform conventions
  • Dependency versions
  • Architectural constraints

Architectural Intent

The services module enforces several architectural rules:

1️⃣ Domain Isolation

Each microservice:

  • Encapsulates its own domain model
  • Does not access another service’s database
  • Communicates via:
    • REST APIs
    • Events (message broker)

This enforces true bounded contexts as described in Domain-Driven Design.


2️⃣ API-First Design

All services follow:

  • OpenAPI specification for REST contracts
  • AsyncAPI for event-driven contracts

This means:

  • APIs are explicit
  • Contracts are versionable
  • Consumer-driven development is possible

Services are not “Spring controllers first” — they are contract-first.


3️⃣ Clean Internal Layering

Although we are not describing each service individually, the internal structure of services follows a consistent pattern:

application/
domain/
infrastructure/
interfaces/
Enter fullscreen mode Exit fullscreen mode

This allows:

  • Business logic isolation
  • Framework independence
  • Testability
  • Technology replaceability

The services module is therefore not just a deployment unit — it is a DDD enforcement mechanism.


4️⃣ Event-Driven Integration

Services are designed to:

  • Publish domain events
  • React to integration events
  • Remain loosely coupled

This enables:

  • Horizontal scalability
  • Independent evolution
  • Failure isolation

The services layer is built assuming distributed system realities:

  • Network failures
  • Eventual consistency
  • Idempotency
  • Observability requirements

Why Not Separate Repositories?

A common question:

Why not split each microservice into its own repository?

Because SmartOrder is a reference platform, not just a production system.

The goal is:

  • To show the blueprint clearly
  • To allow readers to see cross-service patterns
  • To simplify learning and experimentation
  • To enforce architectural coherence

The services module makes the architecture visible.

It turns the repository into:

A living microservices architecture manual.


Governance Through Structure

The services directory enforces governance through:

  • Parent POM inheritance
  • Dependency management alignment
  • Consistent plugin configuration
  • Shared coding conventions
  • Architectural symmetry

This prevents:

  • Version drift
  • Accidental divergence
  • Architecture erosion

The structure itself becomes a control mechanism.


Services as Independent Runtime Units

Even though they live in the same repository, services are:

  • Independently buildable
  • Independently testable
  • Independently deployable
  • Independently scalable

This keeps:

  • CI/CD pipelines clean
  • Docker images separated
  • Infrastructure definitions modular

The mono-repo is organizational.

The services remain microservices at runtime.


Strategic Outcome

The services module achieves a balance between:

Concern Solution
Blueprint clarity Single structured repository
Runtime independence Separate Spring Boot services
Architectural governance Parent POM + enforced layering
Scalability Event-driven + REST integration
Evolvability Domain isolation

This makes SmartOrder:

  • Educational
  • Production-ready
  • Architecturally opinionated
  • Extensible

What’s Next?

In the next articles, we will:

  • Explore individual services
  • Analyze their domain boundaries
  • Examine REST and event contracts
  • Discuss persistence strategies
  • Deep dive into integration patterns

The goal is to progressively unfold the SmartOrder architecture layer by layer.

Stay tuned 🚀

Top comments (0)