Back to Home

Cloud Services and Architectural Complexity

How do we manage the architectural complexity that inevitably arises from using cloud services?

Using Functions as a Service (FaaS) and Backends as a Service (BaaS) (i.e. Serverless) can cause pieces of business logic to be strewn about in many places:

  • Visual orchestration tools (e.g. Azure's Logic Apps)
  • FaaS logic (e.g. AWS Lambda, Azure Functions, Google Cloud Functions)
  • BaaS setups (e.g. Google Firebase)
  • Cloud configurations

Combined with modern single-page applications that maintain complex logic on the client side and microservices that split an organization's concerns across small modules, it's very easy for business logic to become fragmented and intertwined with infrastructure concerns. Is it possible to build organized and maintainable systems using Domain Driven Design (DDD) techniques and clean architecture patterns?

Forest for the Trees

Although originally introduced by Eric Evans in his seminal book in 2004, DDD is very well-suited for reigning in the modern chaos. The guiding principle of this technique is that one must strive to understand the business domain and its interactions, first and foremost, and articulate it in a way that is clear to everyone involved, including the business, developers, managers, etc.—everyone. Clear understanding and communication of the business domain is a firm prerequisite.

A lower-level DDD event diagram showing specific business logic

The result of a DDD approach should be a clear understanding of the business logic (i.e. domain logic) and how that logic is grouped (into domains, subdomains, bounded contexts, and aggregates). Only when the implementation-agnostic groupings (i.e. the domain model) are defined in this way should the discussion turn towards choosing particular technologies. These choices may lead to refining the lower-level specifics of the model (such as reorganizing or splitting an aggregate), but should not modify the higher-level domain architecture. The domain architecture reflects the driving purpose of the business itself and should not be defined by implementation details.

Into the Weeds

DDD provides an excellent organizational approach to the system as a whole, but how do we keep the domain logic separate from the implementation details? Within an individual component of the design (e.g. a hosted application, FaaS, external service, SPA), separating the business logic from the infrastructure can be accomplished using various patterns. I prefer Clean Architecture, coined by "Uncle Bob", but the idea has been around for a long time as iterations of layered, hexagonal, and onion architectures. The key concept behind all these patterns is that the business logic should not depend on anything but itself. It does not make direct API calls; it does not query a database; it does not read a configuration file; it does not concern itself with anything infrastructure related at all—all of these things support and depend on the business logic, not the other way around. Anything that the business logic needs is passed into it, and anything it needs to communicate is sent out via an interface or abstraction it defines.

Clean Architecture: Domain, Application, Presentation, Infrastructure, and Persistence Layers

Clean architecture aligns well with code-based components like applications, microservices, and cloud functions, but how do we apply the same principle to visual or configuration-based components like Azure Logic Apps? There's no easy answer to this because it heavily depends on the particular component; the important thing is that the business logic is separated as much as possible from any external concerns so it can be maintained separately. For instance, a Logic App may retrieve data from an external API in order for the business logic to decide to take an action. The business decision process should be configured in a separate Logic App or Function, rather than embedded within the parent Logic App, and it should be made clear which is which.

Is Smaller Better?

Cloud services provide many benefits, but care must be taken to ensure that the domain logic doesn't become entangled with infrastructure concerns. The smaller pieces of business logic that are spread across various services, the more difficult this becomes. Smaller services should only be used if there is a clear benefit. This concept is a balancing factor to the current trend of implementing smaller and smaller microservices and the adoption of FaaS. A clean domain promotes simplicity, maintainability, and flexibility at the highest level.

People at the Core

Human discipline is required to maintain organization and cleanliness throughout the life of the domain. Because modern architectures are dependent on myriad implementations, there is no guarantee that each technology will have a technological or automated solution to enforce these practices. This brings us back full circle to the importance of everyone understanding the business domain as articulated by DDD. Policies and practices should encourage people to maintain an organized domain, rather than to take short cuts; they should guide people to "fall into the pit of success" as they say, and this starts with clear communication.

Related Posts

The essential design concepts I use when developing an evolvable, distributed system.

Read More

How can we continuously integrate small changes while practicing acceptance test-driven development?

Read More

TDD and Testing Behavior

January 24, 2024

The importance of testing behavior when using test-driven development

Read More

When is it appropriate to use centralized orchestration versus event-driven choreography?

Read More

When defining a business problem and planning its solution, keep the two conversations separate...

Read More

Modern message brokers provide many important benefits to a distributed system...

Read More

Printable cheat sheets to help remember some of Uncle Bob's valuable contributions to the industry

Read More

Why Terraform?

December 25, 2019

Terraform leads the way in the infrastructure-as-code world...

Read More

I was looking for a quick and easy way to put together a personal static site and...

Read More

A few weeks ago, I decided to try Svelte's Sapper framework to handle the front-end of a simple app...

Read More

After years of consulting, I find myself continually coming back to three basic principles of system design...

Read More

In this fifth and final part of the Go middleware tutorial series, we'll use what we've learned to create a more structured API example...

Read More

Go Middleware - Part 4

February 24, 2019

In this fourth part of the Go middleware tutorial series, we'll discuss passing custom state along the request chain.

Read More

Go Middleware - Part 3

February 15, 2019

In this third part of the Go middleware tutorial series, we'll quickly look at a common variant on the recursive middleware implementation from part 2.

Read More

Go Middleware - Part 2

February 9, 2019

In this second part of the Go middleware tutorial series, we'll cover a recursive approach that provides a couple benefits beyond the simple loop chain example from part 1.

Read More

Go Middleware - Part 1

February 6, 2019

This is the first in a series of simple tutorials explaining the usage of HTTP middleware in Go.

Read More

This Old Blog

January 20, 2019

I've decided to resurrect this old blog to publish some nuggets about software architecture and development, and perhaps...

Read More

Drupal 6 Theme Info Error

September 14, 2011

Recently one of my client sites had an issue where the custom theme info was corrupted...

Read More

Here's a slight modification to the handy Google Bookmarks Bookmarklet...

Read More

While building a Drupal site for one of my clients, I was having a heck of a time integrating...

Read More