Skip to main content

System Overview

StreamLine Logistics implements a microservices architecture to manage the complete order lifecycle from creation to final delivery. The system ensures stock consistency and provides real-time package tracking capabilities.

Core Services

The platform consists of three domain-driven microservices:
  • Order Service - Manages customer orders and order lifecycle
  • Inventory Service - Controls product stock and reservations
  • Tracking Service - Records shipment events and delivery status

Hexagonal Architecture (Ports & Adapters)

All microservices follow the Hexagonal Architecture pattern (also known as Ports and Adapters), which provides clear separation between business logic and external concerns.
Hexagonal Architecture organizes code into three main layers:1. Domain Layer (Core)
  • Contains pure business logic and domain models
  • No dependencies on frameworks or infrastructure
  • Implements domain rules and validations
  • Uses immutable entities and value objects
2. Application Layer (Use Cases)
  • Orchestrates domain objects to fulfill use cases
  • Defines input and output ports (interfaces)
  • Implements application services
  • Handles cross-cutting concerns like transactions
3. Infrastructure Layer (Adapters)
  • Implements ports defined in application layer
  • Contains framework-specific code
  • Input adapters: REST controllers, message consumers
  • Output adapters: Database repositories, external API clients
This separation allows you to:
  • Test business logic without databases or web servers
  • Swap infrastructure implementations easily
  • Delay technical decisions (database choice, messaging, etc.)
  • Keep domain logic clean and focused

Package Structure Example (Inventory Service)

com.microservice.inventory/
├── domain/                          # Core business logic
│   ├── model/
│   │   ├── Product.java            # Domain entity
│   │   ├── Stock.java              # Domain entity
│   │   └── ProductDetails.java     # Aggregate
│   ├── valueobject/
│   │   ├── Sku.java                # Value object
│   │   └── Price.java              # Value object
│   └── exception/
│       └── DomainException.java

├── application/                     # Use cases & ports
│   ├── port/
│   │   ├── input/                  # Use case interfaces
│   │   │   ├── CreateProductUseCase.java
│   │   │   ├── GetProductDetailsUseCase.java
│   │   │   └── UpdateStockUseCase.java
│   │   └── output/                 # Repository interfaces
│   │       ├── ProductRepository.java
│   │       └── StockRepository.java
│   ├── service/                    # Use case implementations
│   │   ├── CreateProductService.java
│   │   ├── GetProductService.java
│   │   └── UpdateStockService.java
│   ├── dto/
│   └── mapper/

└── infrastructure/                  # Framework & adapters
    └── adapter/
        ├── input/
        │   └── rest/               # REST API (input adapter)
        │       ├── ProductController.java
        │       └── mapper/
        └── output/
            └── persistence/        # Database (output adapter)
                ├── Product/
                │   ├── ProductEntity.java
                │   ├── JpaProductRepository.java
                │   └── ProductRepositoryAdapter.java
                └── Stock/
Key Principle: Dependencies point inward. The domain layer has zero external dependencies. Infrastructure depends on application, which depends on domain.

Technology Stack

ComponentTechnologyPurpose
FrameworkSpring Boot 3.xMicroservice foundation
Service DiscoveryNetflix EurekaDynamic service registration
API GatewaySpring Cloud GatewayUnified entry point (port 8080)
Config ServerSpring Cloud ConfigCentralized configuration (port 8888)
CommunicationOpenFeignSynchronous inter-service calls
DatabasesPostgreSQL, MySQL, MongoDBPolyglot persistence
OrchestrationDocker ComposeMulti-container deployment

Infrastructure Components

Eureka Server

Port: 8761Service registry and discovery. All microservices register themselves on startup and can discover other services dynamically.

Config Server

Port: 8888Centralized configuration management. Stores YAML configuration files for all services, supporting environment-specific profiles.

API Gateway

Port: 8080Single entry point for external clients. Handles routing, security, and cross-cutting concerns like authentication and rate limiting.

Database Per Service Pattern

Each microservice owns its database to ensure loose coupling and independent scalability:
Why PostgreSQL?
  • ACID compliance for transactional integrity
  • Strong support for complex relationships (orders, order items, customers)
  • Excellent performance for relational data
Database: orderdb
Why MySQL?
  • High performance for read/write operations on master data
  • Proven reliability for product catalogs
  • Excellent concurrent access handling
Database: inventorydb
Why MongoDB (NoSQL)?
  • Schema-less design for flexible event data
  • Different tracking events need different fields (GPS coordinates, photos, customs data)
  • High write throughput for event streaming
  • Natural fit for event sourcing patterns
Database: trackingdb
Database Isolation: Services communicate ONLY through APIs, never by directly accessing another service’s database. This prevents tight coupling and maintains service autonomy.

Service Communication Flow

The system uses synchronous communication via OpenFeign for Phase 1:
1

Client Request

User sends order creation request through API Gateway
2

Stock Validation

Order Service calls Inventory Service via Feign to validate and reserve stock
3

Shipment Initialization

If stock is available, Order Service calls Tracking Service to create shipment
4

Response

Client receives order ID and tracking number

Service Endpoints

ServicePortBase PathEureka Name
Order Service8090/api/v1/ordersmsvc-order
Inventory Service9090/api/v1/inventorymsvc-inventory
Tracking Service8091/api/v1/trackingmsvc-tracking

Design Decisions

  • Simplifies dependency management across services
  • Single docker-compose.yml for entire stack
  • Easier to maintain shared contracts and DTOs
  • Unified versioning and deployment
  • Loose coupling: Schema changes don’t affect other services
  • Independent scaling: Scale only the databases that need it
  • Technology freedom: Choose the best database for each domain
  • Fault isolation: Database failure affects only one service
  • Guarantees identical dev and production environments
  • Orchestrates 3 databases + N services with single command
  • Simplifies networking between containers
  • Easy to add new infrastructure components

Next Steps

Inventory Service

Deep dive into hexagonal architecture implementation

Order Service

Order management and lifecycle

Tracking Service

Shipment tracking and events

Build docs developers (and LLMs) love