Fundamentals 24 min read

Software Complexity and Domain‑Driven Design: Isolating Business and Technical Concerns

This article explains how software complexity arises from both business requirements and quality attributes, discusses the challenges of technical and business complexity, and demonstrates how Domain‑Driven Design—through layered and hexagonal architectures, bounded contexts, and micro‑service decomposition—helps isolate concerns and manage complexity in large systems.

Continuous Delivery 2.0
Continuous Delivery 2.0
Continuous Delivery 2.0
Software Complexity and Domain‑Driven Design: Isolating Business and Technical Concerns
Why still talk about software architecture? As an organizational capability, continuous delivery 2.0 requires not only tooling support and process management but also a solid software architecture; architecture directly influences delivery capability and is tightly linked to system design complexity. This site therefore publishes articles on software architecture to serve readers.

Whether the difficulty stems from scale‑induced comprehension barriers or from unpredictable changes, the ultimate deciding factor is demand. Eric Evans notes that most application complexity lies not in technology but in the domain itself, user activities, or business rules. Hence Domain‑Driven Design (DDD) focuses on the domain and its logic because software systems fundamentally deliver business‑value domain functions to customers.

Complexity Caused by Requirements

Requirements can be divided into business requirements and quality‑attribute requirements, leading to two kinds of complexity: technical complexity and business complexity.

Technical complexity originates from quality attributes such as security, high performance, high concurrency, and high availability. These attributes often conflict—for example, adding security layers (firewalls, encryption, authentication) can increase latency, while scaling for high concurrency may require horizontal partitioning and asynchronous processing, which in turn adds architectural layers and challenges data consistency.

Business complexity grows with the scale of customer needs. As functionality expands, inter‑dependencies increase, affecting maintainability and extensibility. Poor communication or unclear requirements can cause frequent changes, leading to tangled business logic and a "big ball of mud".

Consider an e‑commerce promotion‑rule scenario: different customers and products receive varied promotions (points, coupons, gifts) with customizable periods (e.g., Double‑11). Without careful design, the interaction between promotion rules, products, customers, payments, logistics, and warehousing becomes extremely difficult.

The problem domain becomes too large, making solution discovery harder; this relates to system scale.

Developers conflate business‑logic complexity with technical‑implementation complexity; this relates to system structure.

Growing and changing requirements make it hard to control both business and technical complexity; this relates to system evolution.

DDD provides specific countermeasures for these three issues.

DDD Countermeasures

Isolating Business Complexity from Technical Complexity

To avoid mixing concerns, the first step is to define clear boundaries between business logic and technical implementation. In an e‑commerce order domain, business rules include order validation, total calculation, and workflow, while technical concerns ensure data consistency and correct inter‑service communication.

Business logic should not care how technology is implemented; as long as requirements stay the same, the rules remain unchanged. Ideally, business rules and technical implementation are orthogonal.

DDD achieves this isolation through layered architecture and the Hexagonal Architecture.

Layered Architecture – Concern Separation

The layered approach places business concerns in the Domain Layer, technical concerns in the Infrastructure Layer, and introduces an Application Layer that acts as a façade for use‑cases and as the glue between domain and infrastructure.

The diagram below (original) shows a typical DDD layered architecture where blue areas represent business logic and gray areas represent technical implementation, converging in the Application Layer, which binds them via direct dependencies or Dependency Injection (DI).

Hexagonal Architecture – Inside‑Outside Separation

Proposed by Cockburn, the Hexagonal Architecture places the core business logic at the center and isolates it from technical details via ports and adapters, making the system easier to evolve and keeping technical changes from contaminating the domain.

Case: Isolating Database and Cache Access

DDD recommends defining a Repository abstraction in the domain layer; its implementations live in the infrastructure layer. Switching from MyBatis to Spring Data does not affect domain code.

<span style="color: rgb(86, 156, 214)">package</span> practiceddd.ecommerce.ordercontext.application;

<span style="color: rgb(155, 155, 155)">@Transaction</span>
<span style="color: rgb(86, 156, 214)">public class</span> OrderAppService {
  <span style="color: rgb(155, 155, 155)">@Service</span>
  private PlaceOrderService placeOrder;
  // ...
}

<span style="color: rgb(86, 156, 214)">public interface</span> OrderRepository {
  List<Order> forBuyerId(Identity buyerId);
  void add(Order order);
}

<span style="color: rgb(86, 156, 214)">public class</span> OrderMybatisRepository implements OrderRepository {}
<span style="color: rgb(86, 156, 214)">public class</span> OrderSpringDataRepository implements OrderRepository {}

Cache access follows a similar pattern but belongs to the application layer because it is not domain‑specific.

<span style="color: rgb(86, 156, 214)">public interface</span> CacheClient<T> {
  Optional<T> get(String key);
  void put(String key, T value);
}

<span style="color: rgb(86, 156, 214)">public class</span> RedisCacheClient<T> implements CacheClient<T> {}

Bounded Context – Divide and Conquer

When a problem domain becomes large, DDD introduces Bounded Contexts to split it into smaller, loosely coupled sub‑systems, each with its own model and architecture. This reduces overall complexity and clarifies boundaries.

Example: an international tax‑reporting system originally split only by user roles (Front End for Assignees, Office End for Admins). This led to a massive codebase, duplicated logic, data redundancy, complex bi‑directional integration, and poor adaptability to new requirements.

Applying DDD, the system was re‑modeled into multiple Bounded Contexts such as Account Management, Calendar Management, Work Record Management, File Sharing, Consent, Notification, and Questionnaire. Each context became a micro‑service with its own REST API, allowing independent deployment, dedicated feature teams, and clearer integration.

Domain Model – Abstracting Domain Knowledge

A domain model captures concepts, rules, and relationships of the business. It provides a shared language, reduces ambiguity, and isolates business details from technical implementation, making the system more adaptable to change.

Case: Project Management System Domain Model

The system must support various project‑management methodologies (Waterfall, RUP, XP, Scrum). By analyzing commonalities, a unified domain model was derived, featuring entities such as Phase, Release, Iteration, Activity, Task, and Specification. This model guides implementation and eases the addition of new lifecycle types.

When product managers requested a plan‑template feature, the model was extended with a LifeCycleSpecification concept (a Specification pattern) to encapsulate lifecycle constraints.

In summary, by applying DDD principles—isolating concerns via layered/hexagonal architectures, defining bounded contexts, and building expressive domain models—software teams can tame both technical and business complexity, achieve high cohesion and loose coupling, and evolve large systems with confidence.

Author Introduction

Zhang Yi , architecture practitioner, IT cultural worker, big‑data platform architect, lover of OO and FP, passionate about programming languages, dedicated to combining mainstream DDD with functional, reactive, and micro‑service architectures. Follow his WeChat public account "Yi Yan" or visit his blog at http://zhangyi.xyz .

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.

Software ArchitectureMicroservicesDomain-Driven Designlayered architecturecomplexity
Continuous Delivery 2.0
Written by

Continuous Delivery 2.0

Tech and case studies on organizational management, team management, and engineering efficiency

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.