A group of royal architects and cartographers gather around a massive illuminated kingdom map inside an ornate medieval planning hall. Using compasses, rulers, and drafting tools, they carefully define provincial boundaries that follow rivers, mountains, and trade routes. Shelves filled with scrolls, blueprints, and surveying instruments surround the room, while stained-glass windows reveal a thriving kingdom beyond. The detailed fantasy scene symbolizes thoughtful software architecture by illustrating the deliberate planning and organization required to establish meaningful boundaries within a growing system.
The Architect's Grimoire

Dividing the Kingdom: Finding the Right Boundaries

A realm divided too soon may fall before it ever grows.

Software architecture has a way of making every difficult decision appear deceptively simple. A whiteboard fills with neatly drawn boxes connected by clean lines, and suddenly an application that once fit comfortably into a single project has become a collection of independent services. Every box promises greater flexibility, cleaner organization, and limitless room for future growth. Years spent designing, maintaining, and repairing production systems eventually teach every architect the same lesson. Every boundary carries a cost that will be paid long after the diagram has been erased.

This week, as we continue Designing the Realm, we are moving beyond the roads that connect our systems and into the cities themselves. Roads determine how information travels. Boundaries determine where responsibilities begin and end. Drawing those lines may be one of the most important decisions an architect ever makes because every feature, every deployment, and every developer who inherits the application will eventually encounter them.

Early in my software career, I inherited an application that everyone proudly described as service-oriented. Nearly every feature lived in its own project. Customer management, authentication, reporting, notifications, inventory, payments, and document generation each occupied a separate repository with an independent deployment. At first glance, the architecture looked remarkably sophisticated. Within a few weeks, however, I realized that adding a relatively simple feature required coordinated changes across six repositories, multiple deployment pipelines, and several teams, all of which had to release on the same afternoon.

The problem was not the number of services. The real problem was that none of them were truly independent. Nearly every meaningful enhancement crossed several architectural boundaries because those boundaries had been drawn based on technical preferences rather than natural business responsibilities. What appeared elegant in architectural diagrams became frustrating during everyday development.

That project permanently changed how I think about software architecture. I stopped asking whether an application could be divided into smaller pieces and began asking whether it should be. The answer has guided almost every architectural decision I have made since then.

Every boundary should remove more complexity than it creates.

The Cartographer’s Chamber: Borders Should Be Discovered, Not Invented

Every thriving kingdom begins with a single settlement. As its population grows, districts emerge naturally. Merchants gather around marketplaces, scholars establish libraries, craftsmen build workshops, and soldiers construct barracks near the city gates. None of those districts appear because a ruler wanted a prettier map. They appear because people performing related work naturally benefit from living near one another. Software architecture evolves in much the same way.

Many developers believe creating additional projects automatically produces cleaner software. The temptation is understandable because modularity feels organized, and organization feels like progress. Unfortunately, every new module, repository, package, or service introduces another relationship that must be understood, documented, tested, deployed, and maintained. Organization achieved through unnecessary separation often creates more work than it eliminates.

Imagine a young kingdom establishing independent cities for farming, blacksmithing, fishing, baking, and trade long before there are enough citizens to populate even one successful capital. Every city requires roads, guards, walls, taxation, governance, and communication with neighboring settlements. Before long, the kingdom spends more effort maintaining infrastructure than serving its people. Software behaves no differently. Every additional service requires logging, monitoring, authentication, deployment automation, configuration, health checks, backups, and operational ownership before it delivers any direct value to the application’s users.

Whenever I consider introducing another architectural boundary, I pause and ask a simple question. Am I solving a problem that exists today, or am I investing in a future that may never arrive? Good architecture leaves room for growth without requiring every developer to carry the weight of speculative complexity. The future deserves consideration, but it should not dictate every decision made in the present.

Consider a growing business application that manages customers, orders, inventory, invoices, and shipments. The temptation is to split every feature into its own service because each represents an obvious area of responsibility. Yet suppose the application still has only five developers, one release schedule, and a single production deployment. Dividing those responsibilities immediately introduces network communication, distributed logging, deployment coordination, and additional infrastructure, without reducing the business knowledge required to understand the system.

A thoughtfully organized modular application often provides all the architectural benefits the project currently needs.

src/
│
├── Sales/
│   ├── Customers/
│   ├── Orders/
│   └── Invoicing/
│
├── Fulfillment/
│   ├── Inventory/
│   ├── Shipping/
│   └── Warehousing/
│
├── Shared/
│   ├── Security/
│   ├── Logging/
│   ├── Validation/
│   └── Common/
│
└── Program.cs

Notice what this structure accomplishes. Responsibilities remain clearly separated, yet communication remains inexpensive because every component still lives inside the same application boundary. Developers can quickly locate the code they need, debugging remains straightforward, and deployment continues as a single predictable unit. The kingdom has well-planned districts protected by a common wall, rather than scattered villages struggling to coordinate over great distances.

The important observation is that none of these modules were created because someone anticipated microservices. They were created because the application itself became easier to understand. If the business eventually grows large enough to justify independent services, these modules already provide the seams where that evolution can occur. Good architecture prepares for change without demanding it prematurely.

The Guildmaster’s Rule: Keep What Changes Together

One lesson has consistently proven itself throughout my career. Software that changes together should usually remain together. Developers often confuse application size with architectural weakness, but the two are not synonymous. I have worked inside remarkably large codebases that remained pleasant to maintain because related responsibilities lived side by side. I have also encountered relatively small distributed systems where even the simplest enhancement required touching half a dozen services before the work could be completed.

These days, I rarely begin by studying architecture diagrams. Instead, I ask a developer to explain how they would implement an ordinary feature request. Within a few minutes, I usually know whether the architecture serves the business or has quietly become the business. When routine work demands extraordinary coordination, the boundaries deserve another look.

Strong cohesion keeps related work together because those pieces evolve for the same reasons. Customer validation, customer authorization, customer persistence, and customer business rules generally mature together as the application grows. Separating them by technical category often improves communication while reducing clarity. Developers start thinking about infrastructure rather than the problem they are trying to solve.

The difference becomes obvious when examining code. Imagine beginning with an oversized service responsible for nearly every aspect of order processing.

</> C#

public async Task<Order> CreateOrder(CreateOrderRequest request)
{
    ValidateCustomer(request.CustomerId);

    ValidateInventory(request.Items);

    ProcessPayment(request.Payment);

    CreateShipment(request.Address);

    SendConfirmationEmail(request.Email);

    SaveOrder(request);

    return order;
}

At first glance, everything seems convenient because all operations occur in one place. Over time, however, this method begins changing for dozens of unrelated reasons. Shipping policies evolve independently from payment processing. Email templates change without affecting inventory rules. Warehouse logic evolves under different business pressures than customer validation does. One method gradually becomes responsible for nearly everything.

Rather than immediately separating each concern into an independent service, I prefer to separate responsibilities within the application first.

</> C#

public async Task<Order> CreateOrder(CreateOrderRequest request)
{
    Customer customer =
        await _customerService.ValidateAsync(request);

    Order order =
        await _orderingService.CreateAsync(customer, request);

    await _fulfillmentService.PrepareShipmentAsync(order);

    await _notificationService.SendConfirmationAsync(order);

    return order;
}

Notice what changed. The responsibilities became clearer, but the application itself did not become distributed. Each module now owns a meaningful portion of the workflow while remaining inexpensive to evolve. If one of those modules eventually grows into an independently governed service, extracting it later becomes significantly less disruptive because the architectural seam already exists.

The Council of Many Crowns: When Independence Finally Makes Sense

Microservices have earned their place in modern software architecture because they solve real problems. Unfortunately, they have also become one of the most frequently misunderstood architectural patterns in our profession. I have watched development teams adopt them because successful technology companies used them, because conference speakers praised them, or because they sounded like the inevitable destination of every growing application. None of those reasons justify dividing a kingdom.

A microservice is not defined by how many lines of code it contains. Its defining characteristic is independence. It has full responsibility for the business area, controls its own data, follows its own deployment schedule, and supports a team capable of maintaining it without constant coordination with neighboring systems. Independence, not size, is what transforms a well-designed module into a true service.

That distinction matters because distributed systems introduce costs that never appear in architecture diagrams. Network latency replaces direct method calls. Temporary outages become routine design considerations. Logging must span multiple applications. Authentication extends beyond the application’s boundary. Version compatibility becomes an ongoing responsibility instead of an occasional concern. Every deployment requires more planning because one service may now depend upon another being available at exactly the right moment.

I often compare this transition to a prosperous province becoming its own kingdom. Such a province maintains its own treasury, protects its own borders, governs its own cities, and possesses enough experienced leadership to thrive independently. Declaring independence succeeds because the province already behaves like a kingdom. Announcing that every small village is now its own realm accomplishes little beyond multiplying bureaucracy.

One organization I worked with learned this lesson in a particularly memorable way. Their customer management system had been divided into separate services for customer profiles, customer preferences, addresses, loyalty rewards, communication settings, and authentication. Every customer update triggered a chain of network requests that crossed nearly the entire architecture. During one production incident, a temporary slowdown in the preferences service prevented customers from updating their mailing addresses because every operation depended upon every other service completing successfully. The architecture looked wonderfully modular until a single delayed response exposed how tightly everything remained coupled.

True independence is measured by how little neighboring systems need to know about one another. If every deployment requires coordination across the application, the borders may exist on paper while the responsibilities remain hopelessly intertwined.

The Royal Surveyors: Following the Shape of the Land

Thoughtful architectural boundaries are almost never invented during planning meetings. More often, they reveal themselves gradually as the application matures. Certain areas of the system begin changing more frequently than others. Individual teams develop specialized expertise around specific capabilities. Performance requirements begin diverging. Regulatory obligations appear in one part of the application while leaving the remainder untouched. The application quietly reveals where new borders belong.

One habit I encourage every developer to cultivate is watching why code changes instead of merely observing what changes. Requirements rarely arrive at random. Patterns emerge over months and years. Modules that evolve together usually belong together. Components that consistently change for entirely different business reasons may eventually deserve independent governance.

Suppose an online retailer begins with a single modular application responsible for products, inventory, customers, orders, payments, and shipping. During the first several years, all features evolve together because the business itself remains relatively small. Eventually, however, payment processing becomes subject to financial audits, security certifications, fraud detection, and increasingly specialized compliance requirements. Dedicated engineers begin maintaining that portion of the application while the remainder of the development team focuses elsewhere.

At that point, the architecture has learned something valuable. Payments are no longer simply another module. They have become a distinct operational responsibility with unique deployment requirements, independent expertise, and business rules unlike anything else inside the application.

The architectural evolution now becomes obvious.

Year 1

+--------------------------------------+
|          Modular Monolith            |
|                                      |
| Customers  Orders  Inventory         |
| Payments   Shipping                  |
+--------------------------------------+

                │
                │ Business Growth
                ▼

Year 3

+--------------------------+
| Core Business System     |
| Customers                |
| Orders                   |
| Inventory                |
| Shipping                 |
+--------------------------+

            REST API

+--------------------------+
| Payment Service          |
| Fraud Detection          |
| Compliance               |
+--------------------------+

Notice what happened. The payment service did not emerge because someone predicted future scalability. It emerged because the business naturally evolved into two distinct operational domains. Reporting and notifications might remain inside the modular application for years because they still share the same deployment cadence and operational needs as the rest of the system. Payments, however, have begun following a different path. The architecture simply acknowledges what the business has already demonstrated.

That distinction separates thoughtful design from architectural speculation. Rather than forcing boundaries into existence, experienced architects recognize them when they naturally appear.

The Master Mason’s Workshop: Proving the Boundary Before Building the Castle

One practice has served me remarkably well throughout my career. Before extracting anything into its own service, I first prove that the boundary works inside the existing application. Modules cost very little to reorganize. Services cost considerably more because every adjustment now affects infrastructure, deployments, monitoring, testing strategies, documentation, and operational ownership.

Treating modules as architectural apprentices allows boundaries to mature before asking them to govern their own kingdoms. If the separation proves awkward, the modules can be reorganized quickly with relatively little disruption. If the separation continues to demonstrate clear ownership after months or years of development, promoting that module as an independent service becomes far less risky because the application has already validated the design.

Years later, I worked on another enterprise application that remained a modular monolith for nearly a decade. Every few years, someone proposed breaking it into microservices. Each architectural review reached the same conclusion. The application continued to grow comfortably because the business itself still behaved as a single cohesive system. The architecture succeeded precisely because it resisted changing before the business required it. That experience reinforced something I first learned much earlier in my career. Patience is often one of the architect’s most valuable design tools.

Dependency inversion plays an important role in preparing for future growth. When components communicate through stable interfaces rather than concrete implementations, the application quietly gains flexibility without incurring the operational costs of distributed systems.

</> C#

builder.Services.AddScoped<ICustomerService, CustomerService>();
builder.Services.AddScoped<IOrderingService, OrderingService>();
builder.Services.AddScoped<IFulfillmentService, FulfillmentService>();
builder.Services.AddScoped<INotificationService, NotificationService>();

Those registrations appear ordinary, yet they establish one of the most important architectural principles in the application. Every dependency points to an abstraction rather than a concrete implementation. That decision costs almost nothing today, but it provides remarkable flexibility years later when modules inevitably evolve. Whether fulfillment ultimately remains within the modular application or becomes an independently deployed service, the surrounding code requires very little change because the boundary was thoughtfully designed from the beginning.

Some architects worry that delaying the adoption of microservices limits future scalability. Years spent maintaining production systems have consistently shown me the opposite. Well-designed modular applications often scale far beyond expectations while remaining dramatically easier to understand, test, and maintain. They also preserve something that distributed systems frequently sacrifice far too early: developer simplicity.

The Ranger’s Watchtower: Recognizing the Signals of Growth

Enduring kingdoms rarely announce major changes overnight. They grow steadily until familiar patterns begin shifting. Markets become busier. Roads carry more travelers than before. Individual provinces require their own governors because the capital can no longer efficiently manage every local decision. Software follows remarkably similar patterns.

Architectural growth leaves recognizable clues for anyone willing to observe them. One portion of the application begins demanding significantly greater scalability than everything surrounding it. Separate teams naturally emerge around different areas of expertise. Independent release schedules become increasingly desirable because unrelated features continually delay one another. Regulatory requirements begin affecting one subsystem while leaving the remainder untouched. Those signals deserve careful attention because they originate from measurable experience rather than fashionable opinion.

The opposite signals deserve equal attention. If every new feature still requires intimate knowledge of neighboring modules, if every deployment involves nearly the entire development team, or if separating a component simply introduces additional network communication without reducing collaboration, the application may not be ready for another border. Good architecture removes friction. It should never merely relocate it.

The Royal Architect’s Compass: Questions Before Raising New Walls

Over the years, I have accumulated a small collection of questions that I revisit whenever I consider introducing a significant architectural boundary. They are neither revolutionary nor particularly complicated, yet they have prevented more unnecessary complexity than any framework or design pattern I have ever adopted. Architecture is rarely improved by asking whether something can be separated. It is improved by asking whether the separation creates lasting value.

The first question concerns responsibility. Does this portion of the application represent a complete business capability, or am I merely separating technical concerns that continue changing together? Stable boundaries almost always reflect how the business operates because businesses evolve more slowly than technologies do. Frameworks change, libraries mature, and deployment strategies come and go, but the work an organization performs usually remains recognizable for many years.

The second question examines independence. Could this portion of the application realistically be developed, tested, deployed, monitored, and maintained without coordinating every other team? If the honest answer is no, then the application has probably not reached a natural architectural seam. Dividing it today simply replaces inexpensive method calls with expensive network communication while preserving the same dependencies.

The third question focuses on operational responsibility. Every independent service demands monitoring, alerting, logging, backups, security reviews, deployment automation, documentation, and production support. Those responsibilities remain long after the excitement of adopting a new architecture has faded. Successful architects account for those costs before construction begins instead of discovering them after the kingdom has already expanded.

The fourth question considers the future without pretending it can be predicted perfectly. If this application remains exactly as it is for the next several years, will these boundaries continue serving the development team well? Good architecture leaves room for thoughtful evolution without forcing every present-day decision to accommodate an uncertain tomorrow. Flexibility grows from sound design, not from speculative complexity.

These questions rarely provide immediate answers. Instead, they encourage disciplined conversations that prioritize business needs over architectural fashion. That simple shift in perspective has influenced nearly every important design decision I have made during my career.

The Stonecutter’s Proof: Divide the Business, Not the Work

One final comparison illustrates the lesson better than any architectural diagram. Developers sometimes divide applications by technical activities rather than business responsibilities. Although those boundaries appear organized, they often guarantee that every feature must cross multiple services before it can be completed.

Preferred

Order Module
Payment Module
Inventory Module
Shipping Module
Notification Module


Avoid

Order Validation Service
Order Calculation Service
Order Persistence Service
Order Formatting Service
Order Email Service

The first organization separates meaningful business capabilities. Each module represents a recognizable part of the domain and can mature at its own pace. The second separates technical activities that almost always change together. Adding a new order feature now requires coordinating validation, calculations, persistence, formatting, and notifications across multiple components, even though the business still treats them as a single responsibility.

Good boundaries simplify the work developers perform every day. Poor boundaries merely move that work somewhere else.

The Kingdom That Grew One Province at a Time

One of my favorite architectural diagrams is also one of the least impressive at first glance. It does not showcase dozens of interconnected services, elaborate cloud infrastructure, or colorful deployment pipelines stretching across multiple regions. Instead, it illustrates something far more valuable. It shows an architecture that grew naturally, with each new service appearing only after the existing design could no longer support the business’s evolving needs.

Year 1

+--------------------------------------+
|          Modular Monolith            |
+--------------------------------------+

                 │
                 │ Growth Driven by Business
                 ▼

Year 3

+-------------------------+
| Core Business System    |
+-------------------------+

          │

          ├────────────── REST API ──────────────┐
          ▼                                      ▼

+----------------------+             +----------------------+
| Payment Service      |             | Reporting Service    |
+----------------------+             +----------------------+

                 │
                 │ Continued Organizational Growth
                 ▼

Year 6

+-------------+  +-------------+  +---------------+  +--------------+
| Core System |  | Payments    |  | Reporting     |  | Notifications|
+-------------+  +-------------+  +---------------+  +--------------+

Nothing about the original modular monolith was a mistake. It provided exactly the level of organization the business required during its early years. As new operational needs emerged, the architecture evolved one capability at a time rather than attempting to anticipate every future requirement from the outset. The architecture evolved because the business demanded it, not because the diagram looked incomplete.

That progression reflects one of the most important truths I have learned as an architect. Monoliths and microservices are not opposing philosophies competing for supremacy. They are different points along a continuum of architectural maturity. Some applications will remain successful as modular monoliths throughout their entire lifespan because their businesses never require greater separation. Others will gradually evolve into collections of independently governed services because organizational growth eventually makes that transition worthwhile. Neither outcome is inherently better. The success lies in allowing the software to mature at the same pace as the organization it serves.

The Crown’s Final Lesson

When newer developers ask me how to recognize good software architecture, they often expect a discussion of frameworks, cloud platforms, messaging technologies, or deployment strategies. Those tools certainly matter, but they are rarely the defining characteristic of an enduring system. Instead, I encourage them to observe how experienced teams actually work. Watch how confidently they introduce new features. Notice how naturally responsibilities fit within the existing design. Pay attention to how rarely the architecture itself becomes the obstacle standing between developers and the work their users genuinely need.

The strongest kingdoms are not remembered for possessing the most borders, castles, or provinces. They are remembered because every road connected meaningful destinations, every city served a purpose, and every boundary protected something worth building. Software architecture follows exactly the same principle. Thoughtful boundaries reduce confusion, strengthen cohesion, and allow every part of the application to contribute to something larger than itself.

As we explored this week’s theme, Designing the Realm, we continued building upon the foundation established during Foundations of the Kingdom. Strong castles alone do not create enduring kingdoms. They also require trusted roads, meaningful borders, and architects disciplined enough to build only what today’s kingdom truly needs while preparing wisely for tomorrow. Every lesson in this series builds upon the last because good architecture is never the product of a single decision. It is the accumulation of hundreds of thoughtful ones.

As architects, our responsibility extends well beyond writing elegant code. We shape environments where future developers will spend years building, maintaining, and extending software that may long outlive our own involvement. Every unnecessary boundary becomes another obstacle someone else must navigate. Every thoughtful boundary becomes an investment that quietly pays dividends with each subsequent feature delivered. Remembering that responsibility has made me considerably more cautious than I was early in my career, and I believe it has also made me a better architect.

On Friday, we will leave the kingdom’s borders behind and step inside its most heavily guarded chamber. The Royal Treasury: Protecting the Kingdom’s Data will explore why information is the kingdom’s greatest asset, how thoughtful data architecture preserves both security and integrity, and why even the strongest walls mean little if the treasury itself is left unprotected.

Kingdoms are rarely remembered because they had the greatest number of borders. They are remembered because every border protected something worth building. Software architecture is no different. The boundaries we draw today should make tomorrow’s work simpler, not merely more impressive.

Leave a Reply

Your email address will not be published. Required fields are marked *