Why Bounded Contexts Are the Real Basis for Splitting Microservices

The article explains how bounded contexts, defined by language boundaries rather than functional ones, guide the logical separation of a payroll system into distinct domains, illustrating the process with concrete terminology lists, ambiguity detection, authority mapping, code examples, and deployment‑agnostic module structures.

Tinker Programmer
Tinker Programmer
Tinker Programmer
Why Bounded Contexts Are the Real Basis for Splitting Microservices

Why a Single User Class Can’t Serve an Entire Company

In a monolithic payroll application, the term "Employee" appears in HR, attendance, payroll, and access‑control modules, each requiring different attributes such as contract details, schedules, salary data, or card numbers. A single sys_user table grew to over 80 columns, and the resulting User class tried to represent four unrelated business concepts, leading to unclear field relevance, risky changes, and a month‑long onboarding for new developers.

The lesson is clear: when the same word carries different meanings across contexts, it should not share a model. This is the core reason for bounded contexts.

Bounded Context Explained in Plain Language

Many DDD articles define a bounded context as "a clearly bounded area where a unified language applies". In other words, within the boundary each term has a precise, consistent meaning; outside the boundary the same term may mean something entirely different.

For example, "Employee" means a personnel record in HR, a pay‑eligible person in payroll, and a clock‑in participant in attendance. These three meanings require three separate models.

DDD does not aim to eliminate such complexity; it helps you acknowledge and manage it.

Practical Bounded‑Context Splitting of a Payroll System

Step 1: List All Key Business Terms

Extract terms from requirement documents, meeting notes, and the previous glossary: employee, contract, department, position, attendance, schedule, leave, overtime, base salary, performance bonus, social security, housing fund, individual tax, special deductions, payslip, payroll cycle, …

Step 2: Identify Ambiguities – Where the Same Term Means Different Things

"Employee" carries contract information in HR, schedule data in attendance, and salary plan in payroll. "Salary" means net pay to the employee, pre‑tax gross amount for finance, and contribution base for social‑security agencies. Different meanings indicate natural boundaries.

Step 3: Define Boundaries by Data Ownership

The decisive question is: who has the final say over a piece of data?

HR decides on hire date, contract type, and rank → "Employee Information" BC.

Attendance decides on clock‑in records and leave balance → "Attendance" BC.

Payroll decides on salary plan and bank account → "Payroll Calculation" BC.

Tax authorities decide on tax rules and thresholds → "Tax" BC.

Applying this principle yields four bounded contexts with the following simplified context map:

┌───────────────────┐     ┌───────────────────┐
│   Employee Info BC │     │    Attendance BC  │
│                     │     │                   │
│ Employee            │     │ AttendeeProfile   │
│ ContractInfo        │     │ WorkSchedule      │
│ OrgPosition         │     │ LeaveBalance      │
└────────┬──────────┘     └────────┬──────────┘
         │                        │
         └────────────┬────────────┘
                      ▼
               ┌────────────────────────┐
               │   Payroll Calculation BC│ ← Core Domain
               │                        │
               │ Payee                  │
               │ PaySlip                │
               │ SalaryPlan             │
               │ GrossSalary            │
               └────────────┬───────────┘
                            │
                            ▼
               ┌────────────────────────┐
               │        Tax BC          │
               │                        │
               │ TaxRule                │
               │ IndividualTax          │
               │ TaxDeduction           │
               └────────────────────────┘

This context map visualizes business boundaries rather than technical deployment.

Code Illustration of Separate Models

After the split, each BC defines its own class for "Employee" and shares only the EmployeeId as a cross‑BC identifier.

// Employee Info BC – HR perspective
package com.company.hr.domain;
public class Employee {
    private EmployeeId id;
    private PersonName name;
    private IdCard idCard;
    private ContractInfo contractInfo; // type, dates, expiry
    private OrganizationPosition position; // department, role
    private EmploymentStatus status; // active, probation, terminated
}
// Attendance BC – clock‑in perspective
package com.company.attendance.domain;
public class AttendeeProfile {
    private EmployeeId employeeId; // only the ID, no direct object reference
    private WorkSchedule schedule; // shift plan
    private List<DailyAttendance> records; // daily punch‑in/out
    private LeaveBalance leaveBalance; // vacation balances
}
// Payroll Calculation BC – money perspective
package com.company.payroll.domain;
public class Payee {
    private EmployeeId employeeId; // shared ID
    private SalaryPlan salaryPlan; // base salary structure
    private BankAccount bankAccount; // payout account
    private TaxProfile taxProfile; // tax thresholds, deductions
}

Each BC holds only the EmployeeId, avoiding direct dependencies on other BCs' internal models. This isolation prevents cascading changes: a field added to the HR Employee class will not force payroll code to change.

Bounded Context ≠ Microservice

Logical boundaries (BC) are not the same as deployment boundaries (microservices). A BC can be a Maven module, a Java package, or later a separate service. Early in a project, multiple BCs can coexist in a single monolith, gaining the benefits of domain isolation without the operational overhead of distributed systems.

payroll-system/
├── hr-context/          # Employee Info BC (Maven module)
│   ├── domain/
│   ├── application/
│   └── infrastructure/
├── attendance-context/ # Attendance BC
│   ├── domain/
│   ├── application/
│   └── infrastructure/
├── payroll-context/   # Core Payroll BC
│   ├── domain/
│   ├── application/
│   └── infrastructure/
└── tax-context/        # Tax BC
    ├── domain/
    ├── application/
    └── infrastructure/

When scaling is needed, each module can be extracted as an independent service, because the domain packages already have no cross‑references.

Prematurely splitting a monolith into many services often leads to problems such as “creating a payslip requires calls to eight services”, timeouts, and distributed‑transaction headaches—issues caused by unclear boundaries rather than microservices themselves.

How to Judge Whether a BC Split Is Appropriate

How many BCs does a core use case touch? If a single operation (e.g., “pay this month’s salary”) spans more than four BCs, the boundaries may be too fine‑grained or the use case is poorly designed. Ideally, a core operation stays within one or two BCs.

Does changing one BC’s internal logic affect others? If modifying a field in the Attendance BC forces changes in the Payroll BC, the boundary is not clean.

How many BCs does a team member maintain? Conway’s Law suggests a one‑to‑one mapping between teams and BCs. If a three‑person team must maintain six BCs, the split is likely too granular.

Can the BC name be used directly in business meetings? If saying “I’m updating the Payroll Calculation BC’s payout logic” is understood by HR, the naming is appropriate; otherwise the boundary may be technical rather than business‑driven.

Key Takeaways

Language boundaries, not functional boundaries, drive BC division.

The owner with final data authority defines the BC’s scope.

BC ≠ microservice; logical separation precedes deployment decisions.

A well‑scoped BC should allow a core business use case to be completed within it.

Next, we will discuss the four collaboration patterns between bounded contexts and how to choose the right one for a given scenario.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

backend architecturemicroservicesDomain-Driven DesignBounded Contextservice decomposition
Tinker Programmer
Written by

Tinker Programmer

Solving problems with code, sharing practical tech insights, and leveling up together!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.