Designing Service Boundaries

1. Applying Domain-Driven Design

DDD ConceptRole in Microservices
Strategic DesignIdentifies bounded contexts → service boundaries
Tactical DesignAggregates, entities, value objects inside a service
Ubiquitous LanguageAPI contracts mirror business terms
Domain EventsDrive async integration between services
AggregatesTransactional consistency boundary

2. Defining Bounded Contexts

StepAction
1. Event StormingWorkshop to map domain events with experts
2. Identify Pivotal EventsState changes critical to business
3. Group by LanguageWhere terms have one consistent meaning
4. Draw BoundariesEach group = candidate context/service
5. Map RelationshipsCustomer/Supplier, Conformist, ACL, Shared Kernel

3. Identifying Service Domains

Domain TypeTreatmentExample
CoreBuild in-house, invest heavilyFraud detection at a bank
SupportingBuild but minimizeInternal admin tools
GenericBuy or use SaaSAuth (Auth0), Email (SendGrid)

4. Decomposing by Business Capability

CapabilityServiceOwns
Order Managementorder-serviceOrders, line items
Inventoryinventory-serviceStock levels, reservations
Paymentspayment-serviceCharges, refunds
Shippingshipping-serviceShipments, carriers
Notificationsnotification-serviceEmail/SMS delivery

5. Decomposing by Subdomain

ApproachDescription
By CapabilityWhat the business does (verbs)
By Subdomain (DDD)What the business knows (nouns + experts)
When to Pick SubdomainComplex domain logic, expert-driven
When to Pick CapabilityWorkflow-heavy, process-oriented

6. Applying Single Responsibility Principle

SmellRefactor
Service changes for >1 reasonSplit along axes of change
Multiple unrelated tablesLikely 2 services merged
Large team owning one serviceSplit team and service together
High coupling at API surfaceRe-evaluate boundary

7. Determining Service Granularity

HeuristicQuestion
Team SizeCan a 2-pizza team own it?
CohesionDo features change together?
CouplingFew sync calls to others?
DataOwns its data without joins?
LifecycleIndependent deploy cadence?

8. Avoiding Distributed Monoliths

Anti-PatternSymptomFix
Synchronous ChainsA→B→C→D per requestAsync events, choreography
Shared DatabaseMultiple services read same tableAPI ownership, CDC, replicas
Lock-Step ReleasesMust deploy services togetherVersioned, backward-compatible APIs
Shared ModelsCommon DTO library forces redeployEach service owns its DTOs
Cross-Service TransactionsDistributed locks/2PCSagas, eventual consistency

9. Using Database per Service Pattern

AspectDetail
GoalLoose coupling, independent schema evolution
ImplementationSeparate schema, instance, or engine
Cross-Service ReadsAPI calls, read-replicas via CDC, materialized views
Cross-Service WritesSagas, event-driven workflows
Trade-offNo joins, eventual consistency

10. Managing Service Dependencies

Dependency TypeRiskMitigation
Runtime (sync)Cascading failureCircuit breaker, fallback
Runtime (async)Lag, ordering issuesIdempotent consumers, DLQ
Build-time (libs)Coupled releasesStable, versioned shared libs
DataHidden coupling via DBDatabase per service

11. Defining Aggregates and Entities

TermDefinition
EntityObject with identity (e.g. Customer)
Value ObjectImmutable, equality by value (Money, Address)
AggregateCluster of entities/VOs treated as a unit
Aggregate RootOnly entry-point; enforces invariants
RepositoryPersists and reconstitutes whole aggregates

Example: Order aggregate (Java 21)

public class Order { // aggregate root
    private final OrderId id;
    private final List<OrderLine> lines = new ArrayList<>();
    private OrderStatus status = OrderStatus.DRAFT;

    public void addLine(ProductId productId, int qty, Money price) {
        if (status != OrderStatus.DRAFT) throw new IllegalStateException();
        lines.add(new OrderLine(productId, qty, price));
    }

    public Money total() {
        return lines.stream().map(OrderLine::subtotal).reduce(Money.ZERO, Money::add);
    }
}

12. Using Context Mapping Patterns

PatternUse When
PartnershipTwo contexts succeed/fail together
Shared KernelSmall shared model, joint ownership
Customer/SupplierUpstream serves downstream's needs
ConformistDownstream conforms to upstream model
Anti-Corruption LayerTranslate to protect domain integrity
Open Host ServicePublic protocol for many consumers
Published LanguageStable schema (e.g. JSON Schema, Avro)
Separate WaysNo integration; independent solutions