The Boundary Is the Product: What Domain Aggregates Teach Us About Safer Cloud Infrastructure

tfc

Hatched by tfc

Aug 19, 2026

11 min read

94%

0

What if the most important feature of a cloud infrastructure component is not what it creates, but what it refuses to expose?

A reusable infrastructure construct can look deceptively simple. Give it a few inputs, and it creates a database, a queue, a network, or an entire application slice. But beneath that convenience lies a difficult design problem: how much of the component should the outside world be allowed to know, change, or depend on?

This is the same problem that appears in domain driven design when a model is organized into an aggregate. An aggregate is not merely a group of related objects. It is a boundary around business rules, with one root through which the outside world must interact. The boundary protects consistency. The root controls access. The interior can evolve without forcing every external object to understand its details.

That idea offers a surprisingly powerful way to think about community built cloud infrastructure. A well designed construct is not just a bundle of resources. It is an architectural aggregate: a boundary that packages complexity, protects invariants, and exposes one deliberate interface to the systems around it.

The deeper lesson is this: reusability does not come from exposing more capability. It comes from exposing less, but exposing it more intentionally.

The hidden cost of reusable infrastructure

Infrastructure code is often treated as configuration rather than software. That distinction becomes expensive as systems grow. A small application may begin with a queue and a function. Later, it acquires permissions, encryption keys, alarms, retry policies, dead letter handling, network rules, logging, deployment dependencies, and data retention settings. Each addition may be reasonable in isolation. Together, they form a system of coupled decisions.

When those decisions are copied from project to project, the organization creates a kind of accidental public interface. Developers begin to depend on resource names, internal permissions, specific event wiring, or the exact way a construct creates its child resources. A detail that was never meant to be stable becomes a contract simply because someone reached inside it.

This is the infrastructure equivalent of allowing every object in a domain model to modify every other object directly. The system may work at first, but its rules become impossible to enforce consistently. A change in one place creates surprises elsewhere because there is no meaningful boundary around responsibility.

Consider a reusable order processing component. Its internal implementation might include:

  • A message queue for incoming orders
  • A dead letter queue for failures
  • A function that validates and processes messages
  • An encryption key
  • Permissions connecting the function to storage and messaging services
  • Alarms for backlog size and repeated failures
  • Log retention and operational dashboards

A consumer of this component probably does not need to manipulate each of those resources independently. It may only need to submit an order, receive a reference to the processing endpoint, and configure a few business relevant options. If the consumer must understand the queue, function role, encryption key, and alarm topology separately, the component has failed to absorb complexity. It has merely relocated it.

A good construct instead establishes a boundary. It says: these resources belong together for a reason. These relationships must remain valid. You may use the component through this interface, but you should not have to know how its internal machinery works.

The purpose of abstraction is not to hide complexity forever. It is to decide who is responsible for carrying it.

Aggregates and constructs share the same design problem

In domain driven design, an aggregate has two defining properties. It has a boundary, which determines what belongs inside the unit of change, and it has a root, which is the only object outside consumers may reference directly. These rules are not academic decoration. They prevent invalid state from being created through arbitrary internal access.

The same pattern can guide infrastructure design.

The construct boundary defines which cloud resources, policies, and operational behaviors must change together. The construct root is the public object or interface through which consumers configure and use the component. Internal resources remain implementation details unless there is a compelling reason to expose them.

Imagine a construct called OrderProcessing. Its public interface might include:

  • The name of an input event source
  • A destination for successfully processed orders
  • A maximum processing time
  • A retention period for failed messages
  • A method for granting another component permission to submit orders

What should it avoid exposing by default?

  • The exact function role
  • The internal queue implementation
  • The encryption key object
  • The alarm construction details
  • The names of child resources
  • The order in which resources are created

This does not mean those internals can never be accessed. It means access should be treated as an explicit architectural decision rather than a convenience. The more internal objects a construct exposes, the more consumers can form dependencies on its internal structure. The boundary becomes decorative instead of protective.

There is a useful test here: if a consumer needs to know how a component is built in order to use it, the component is probably exposing implementation rather than capability.

This distinction also clarifies what a reusable component should own. An aggregate should contain objects whose rules and consistency belong together. Likewise, a construct should group resources that share a lifecycle, a security posture, and an operational purpose. A queue, its dead letter destination, its encryption settings, and its failure alarms often form a coherent unit. A random collection of unrelated resources does not become a good construct merely because they are declared in the same file.

The right question is not, “What resources can we package together?” It is, “What must remain true about these resources whenever they are used together?”

The three boundaries of a trustworthy component

The most useful extension of the aggregate analogy is to recognize that cloud infrastructure has at least three boundaries. Each boundary protects a different kind of invariant.

1. The domain boundary

This is the boundary of business meaning. An order, payment, shipment, or account is not just a data structure. It carries rules about valid transitions and permitted changes.

For example, an order should not become shipped before it is paid. If every part of the application can modify the order state directly, the rule becomes scattered across handlers and jobs. An aggregate root can enforce the transition through operations such as authorizePayment or markReadyForShipment.

2. The infrastructure boundary

This is the boundary around the cloud resources that implement those business capabilities. It determines which resources are managed together and which operational guarantees the component provides.

A payment component might own its storage, event publication, encryption, permissions, and alarms. Consumers should interact with the capability through stable operations or clearly defined integration points, not through direct manipulation of its tables and roles.

3. The governance boundary

This is the boundary created by the people and processes that maintain reusable components. A community library does not become trustworthy merely because its code is available. It needs review, security checks, ownership, release discipline, and a way to distinguish experimental work from dependable building blocks.

This third boundary is easy to miss. A construct may have a clean interface and sensible internals, yet still be dangerous if nobody verifies its permissions, dependencies, upgrade behavior, or operational defaults. In that sense, stewardship performs for a library what an aggregate root performs for a domain object: it controls what may enter the trusted boundary and protects the invariants that outsiders rely on.

These boundaries reinforce one another. A domain boundary without an infrastructure boundary can be undermined by direct resource access. An infrastructure boundary without governance can package unsafe defaults. Governance without clear component boundaries produces reviewable code that is still difficult to use correctly.

Trustworthy reuse emerges when all three are designed together.

The root is a promise, not a convenience

Many infrastructure libraries treat their public API as a list of available properties. A stronger approach treats it as a promise about what will remain stable and what the component will protect.

Suppose a construct exposes a raw database table because one early consumer needs a custom query. That decision appears helpful. Soon, several applications write directly to the table. They begin to depend on its schema, indexes, throughput settings, and retention behavior. The component can no longer evolve its data model without coordinating with every consumer. The root has lost authority because its interior has become public territory.

A better interface might expose a repository abstraction, an event stream, or narrowly defined access methods. The component can still provide escape hatches for advanced cases, but those escapes should be visibly costly. They should communicate, “You are stepping outside the protected path, and future changes may require your participation.”

This is the infrastructure version of local identity inside an aggregate. An internal entity may matter deeply within the aggregate, but it does not need an independent identity in the wider system. Similarly, an internal role, queue, or table can be essential to the component without becoming a globally meaningful object that every other component references.

The design principle is simple:

Give external systems stable identities for capabilities, not accidental identities for implementation parts.

This improves more than encapsulation. It improves security. If consumers receive only the permissions they need to invoke a capability, rather than broad access to the underlying resources, the blast radius of mistakes becomes smaller. It improves operability because alarms and logging can be changed internally without requiring every application to be rewired. It improves migration because an implementation can move from one managed service to another while preserving the component’s external contract.

A root is therefore not just an entry point. It is a change firewall. It absorbs internal change and prevents that change from propagating through the entire system.

Designing constructs as change firewalls

A practical way to apply this model is to classify every part of a reusable component according to its relationship with change.

Stable capability

This is what consumers genuinely need and what the component is willing to support over time. Examples include an event input, a permission granting method, a service endpoint, or a small set of business relevant configuration options.

Protected invariant

This is what must always remain true. Examples include encryption being enabled, failed messages having a recovery path, a function having only required permissions, or a database and its backup policy sharing an appropriate lifecycle.

Replaceable mechanism

This is how the capability is implemented today. It might be a particular queue, function runtime, storage engine, or network arrangement. Unless the mechanism is part of the contract, it should remain behind the boundary.

Deliberate escape hatch

This is an advanced access path that exists for legitimate reasons but is not the default. It should be documented, reviewed, and designed with the expectation that it creates coupling.

This classification produces better APIs than simply asking which properties are technically available. It forces the designer to distinguish what must be stable from what merely happens to be visible.

It also gives reviewers a concrete checklist. When evaluating a community construct, ask:

  1. What capability does this component represent?
  2. Which resources must change together to preserve that capability?
  3. What invariants does the component enforce automatically?
  4. Which exposed properties are true promises, and which are accidental leaks?
  5. Can the internal implementation be replaced without forcing consumers to rewrite their systems?
  6. Have security and operational defaults been reviewed as carefully as the code itself?

These questions turn reuse from a code sharing exercise into an exercise in responsibility design.

Key Takeaways

  • Design every reusable construct as an aggregate. Define a clear boundary, choose one deliberate public root, and keep internal resources private by default.
  • Expose capabilities, not machinery. Consumers should ask a component to perform a meaningful function rather than manipulate its internal queues, roles, tables, or keys.
  • Write down the invariants. Encryption, least privilege, failure recovery, observability, and lifecycle behavior should be enforced by the component wherever possible.
  • Treat escape hatches as architectural debt. They can be valuable, but every escape creates a dependency on internals and should be documented as such.
  • Include governance in the design of reuse. Review, security checks, ownership, and release discipline are not administrative extras. They are part of the component’s trust boundary.

The largest mistake in infrastructure reuse is to measure success by how much code disappears from application repositories. The better measure is how many dangerous decisions no longer need to be made independently by every team.

A construct library reaches maturity when its components do more than generate resources. They encode decisions, protect invariants, and make the safe path the easy path. A domain model reaches maturity for the same reason: its boundaries ensure that valid behavior is available without making invalid behavior equally convenient.

The future of reusable cloud architecture may therefore depend less on creating ever larger collections of templates and more on learning where not to connect things. The strongest component is not the one with the most knobs, the broadest exposure, or the greatest number of escape routes. It is the one whose boundary is clear enough that teams can move quickly without accidentally taking ownership of complexity they do not understand.

When you design the boundary well, reuse stops being duplication avoidance. It becomes a way of distributing judgment. The component carries the difficult decisions once, and every consumer inherits not just infrastructure, but a carefully guarded promise about how that infrastructure may be used.

Sources

← Back to Library

Hatch New Ideas with Glasp AI 🐣

Glasp AI allows you to hatch new ideas based on your curated content. Let's curate and create with Glasp AI :)

Start Hatching 🐣