Skip to main content
System design · Modularity

System Modularity: Boundaries That Support Change

Design modular systems around business ownership, explicit contracts, controlled dependencies, transaction boundaries and evidence for extracting a service.

Practical engineering guidance · Architects and engineering teams improving an existing application or deciding whether a module should become a service.
Illustrative software engineers reviewing application code together in a daylight studio
Illustrative engineering collaboration

At a glance

What you need to know.

Modularity gives a team a controlled place to change behavior. It starts with responsibilities and dependency rules inside the current application. Separate deployment becomes useful when operating needs justify it and the team can support the additional network, data and recovery boundaries.

Start with a business responsibility

Describe what each proposed module owns using the work people perform: accept an order, reserve inventory, approve an expense or publish a report. Give the module an owner, vocabulary and authoritative records. A folder named utilities is useful for shared mechanics, but it does not define responsibility for a business decision. Walk through a change request and identify which module should approve the new rule. When several modules independently decide the same outcome, the boundary is probably unclear. Record responsibilities that remain together because they require one consistent decision or an atomic update.

Expose an intentional interface

Let a module expose operations and results rather than allowing callers to reach through its implementation. Specify what an operation means, what information it requires and which errors a caller must handle. An inventory operation might reserve a quantity against an order reference; another module should not write directly into the reservation table. Internal interfaces need the same attention to field meaning as public APIs, although they need not use a network protocol. Test the contract from the caller's perspective. A stable interface protects callers when storage, algorithms or supporting dependencies change behind the boundary.

Make dependency direction reviewable

Draw which modules may call which other modules and identify cycles. A cycle can make an apparently small change require coordinated edits across the application. Separate reusable technical mechanisms from business policy so a logging library does not import order rules. Keep the permitted direction visible in package boundaries, code review and automated dependency checks where those checks provide useful protection. Avoid sharing mutable internal objects that give callers an indirect write path. When a shared abstraction changes frequently for unrelated reasons, evaluate whether a smaller interface or some deliberate duplication would create a clearer boundary.

Keep consistency requirements explicit

A module boundary does not remove the need for consistent state. Identify operations that must commit together and those that can finish asynchronously. Inside one database, a transaction may be the appropriate mechanism. Across independent stores, an event, durable pending operation, reconciliation rule or compensating action may be necessary. Document who owns an operation after a timeout and how a repeated request is recognized. If correctness currently depends on editing several tables in one transaction, extracting only one table into a service can break the business invariant. Design the new completion and recovery contract before changing deployment.

Extract a service for a concrete operating need

Separate deployment can help when a module needs independent scaling, a different release cadence, restricted access or a distinct operational owner. Confirm that the benefit exceeds the added work of authentication, network failures, telemetry, deployment coordination and compatibility. First establish the interface within the existing application and test it with real callers. Then plan a reversible move with controlled traffic and comparable results. A modular application can remain one deployable unit when that suits the team. The number of services is not a measure of architectural quality; explain the operational problem each new boundary solves.

Demonstrate that a change stays inside its boundary

Choose a representative change such as a reservation rule or report format. Verify that the owning module can implement it through its supported interface while callers retain their expected behavior. Exercise rejected input, duplicate operations and an unavailable dependency. For an extracted service, test version overlap, a lost response and rollback after partial traffic cutover. Review access to data and administrative controls as well as code imports. Acceptance evidence should show an understandable ownership map, enforceable dependencies and a recovery procedure. Revisit the design when repeated cross-module edits reveal that a business responsibility has moved or expanded.

Before you start.

  • Name each business responsibility, owner and authoritative record.
  • Describe operations, errors and permitted dependency direction.
  • Prevent callers from writing another module's private state.
  • Preserve transaction invariants or design explicit asynchronous recovery.
  • Justify separate deployment with an operating requirement.
  • Prove a representative change, compatibility overlap and rollback.

Common questions.

Does modularity require microservices?

No. Explicit responsibilities, interfaces and dependency rules can exist inside a single application. Moving a module across a network adds deployment and failure concerns. Use that move when independent operation provides a concrete benefit and the team can maintain the resulting boundary.

Should every module have its own database?

Independent storage can reinforce ownership, but it also changes transaction and recovery behavior. Start by controlling who may read and write each record. Separate stores when isolation or operating requirements justify them, and define how cross-boundary work completes and reconciles.

Continue with the right next step.

Technical references

Map this to your workflow.

Bring the users, existing systems, data boundaries and success criteria. We can discuss the scope and the engineering decisions that need evidence.

Discuss your requirement