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
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/
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)