Assemblage overview: step 3 - defining a service architecture

application architecture   architecting   dark energy and dark matter   assemblage   microservice architecture   service architecture  

Step 1 of the Assemblage definition process distills the requirements into a set of system operations that collectively define the application’s behavior and Step 2 identifies the subdomains that implement those system operations. In this article, I describe step 3 of the Assemblage process, which designs the service architecture.

A service architecture is a (mostly) technology neutral description of a microservice architecture consisting of the services, their responsibilities, subdomains, APIs and collaborations. In particular, inter-service communication is described abstractly using, for example, message channels and other enterprise Integration patterns rather than specific technologies such as Apache Kafka topics.

You will learn how to define the service architecture by grouping subdomains to form services and applying the service collaboration patterns to realize system operations that span multiple services. I describe how the architecture is shaped by the dark energy and dark matter forces. Let’s start with an overview of how to define a service architecture.

Overview of designing a service architecture

The service architecture is designed incrementally, one operation at a time. First, you rank the operations by descending importance. Next, you work your way down the list of operations, designing an operation realization. An operation realization is an architecture modification that adds and updates architectural elements - services, operations, event handlers, and message channels - in order to implement an operation. The service architecture is a composition of each operation’s realization.

Let’s look at how to design a service service, starting with ranking system operations.

Rank system operations by descending importance

Your first task is to rank the system operations by descending importance. Let’s first look at why ranking matters and after that I’ll discuss ranking criteria.

Why ranking matters

Since the service architecture is defined incrementally, one operation at a time, the order in which you consider the operations matters. A higher ranked operation will potentially play a much greater role in shaping the architecture. The higher its ranking the more likely it will be that some or all of its subdomains will be unassigned. You will have more freedom in how you assign its subdomains to services. There’s a greater chance of resolving the dark energy and dark matter forces.

How to rank system operations

There are several ways of assessing a system operation’s importance including:

  • High business criticality - important to design these operations so that they are highly available
  • High technical risk - e.g. complexity, large data volumes, high throughput, low latency, etc.
  • Competitive advantage - e.g. important to be able to rapidly experiment and innovate

For example, let’s imagine that you are working on the FTGO application. You’d probably rank those system operations that support the ordering flow (from order creation to delivery) higher than those that implement other functionality. A ranking might look something like:

  1. createOrder()
  2. acceptOrder()

After ranking the system operations, you will work your way down the list of system operations, incrementally creating the architecture, by designing an operation realization for each one.

Designing an operation realization

You will design an operation realization for each system operation. The design of an operation realization occurs in context of the architecture that’s been defined so far. This architecture is composed of the realizations for the previously designed operations. Initially, that architecture is empty. Each operation realization adds, and updates architectural elements such as services, operations, event handlers, and message channels et.

There are potentially multiple ways to implement an operation. I’ll first describe how to implement a system operation in terms of collaborating services. After that, I’ll describe how evaluate and compare the candidate implementations using dark energy and dark matter forces and pick the best one.

Designing a system operation realization

To design an operation realization you need to do two things. First, you must assign the operation’s unassigned subdomains to new or existing services Next, you must also add elements to the services, such as operations, event publishers, and handlers, that facilitate collaboration. Let’s look at how to design an operation realization in more detail.

Assigning an operation’s subdomains to services

The first step of designing an operation realization is to ensure that all of the operation’s subdomains are assigned to services. Some or all of the operation’s subdomain might have already been assigned to services when designing higher ranked operations. You’ll only be able to assign the operation’s unassigned subdomains to services. The lower an operation’s rank the greater the less freedom you will have to assign subdomains. It’s likely that for many low ranked operations, all of their subdomains will already have been assigned. Let’s look at an example from the FTGO application.

About the acceptTicket() operation

Let’s imagine that you are part way through designing the architecture for the FTGO application. You’ve already designed various operations and defined an architecture consisting of the following services:

  • Consumer Service - contains the Consumer Management domain
  • Order Service - contains the Order Management domain
  • Restaurant Service - contains the Restaurant Managementdomain
  • Kitchen Service - contains the Kitchen Management domain

And now, you now want to implement the acceptTicket() operation, which is invoked when a restaurant accepts a Ticket, which represents a consumer’s order, and commits to having it ready for pickup at a particular time.

This operation is implemented by two subdomains: Kitchen Management and Delivery Management. It updates the state of the Ticket in the Kitchen Management subdomain to ACCEPTED and schedules a Delivery in the Delivery Management subdomain.

AssigningacceptTicket()’s subdomains

The Kitchen Management subdomain has already been assigned to a Kitchen Service. We, therefore, need to decide where to assign the Delivery Management subdomain. For this simple example, there’s 5 possible assignments of the Delivery Management subdomain: anyone of the 4 existing services or to a new Delivery Service. A more complex operation that has multiple unassigned subdomains could have many more possible assignments.

To simplify the rest of the discussion, let’s consider just two candidate assignments for the Delivery Management subdomain:

  1. The Kitchen Service
  2. A new Delivery Service

The following diagram shows the two candidate assignments:

Let’s look at how to design the service collaborations for each of these candidate realizations.

Designing the service collaboration for an operation

The second step of designing an operating realization is to determine how the services should collaborate to implement the operation and the add elements that enable the collaboration. The assignment of an operation’s subdomains to services results in an operation that’s either local or distributed. A local operation is one who’s subdomains are in a single service, where as a distributed operation spans multiple services. In the FTGO example, acceptOrder() in candidate #1 is local to the Kitchen Service where as in candidate #2 it’s distributed across the Kitchen Service and the Delivery Service. Let’s first look at how to design the service collaboration for a local operation.

Designing local operations

If all of an operation’s subdomains are assigned to a single service, then the operation is local to that service. The service collaboration for a local operation is very simple. In acceptOrder() candidate #1, the API Gateway simply forwards the acceptOrder() request to the Kitchen Service. The Kitchen Service has an acceptOrder() operation that changes the state of the Ticket to ACCEPTED and creates a Delivery.

Designing distributed operations

If an operation’s subdomains are assigned to two or more services, then the operation is distributed. There are often numerous possible ways to design the service collaboration for a distributed operation: different combinations of the service collaboration patterns and even multiple ways to apply a given pattern. For example, since the acceptOrder() operation in candidate #2 updates entities in two services, it needs to be implemented as Saga, which spans the Kitchen Service and the Delivery Service. One possible saga design is to use choreography-based Saga that updates the state of the Ticket, and publishes a TicketAccepted event, which triggers the creation of a Delivery. You could, however, use an orchestration-based saga instead. Moreover, there are often multiple possible different sequences of steps in a saga. Ideally, you should consider all possible designs.

To simplify the discussion let’s assume that acceptOrder() is implemented by a chorography-based saga:

This saga works as follows:

  1. The API Gateway routes the acceptTicket() request to the Kitchen Service
  2. The Kitchen Service has an acceptTicket() operation that changes the state of the Ticket to ACCEPTED and publishes a TicketAccepted event
  3. The Delivery Service has an event handler for the TicketAccepted event that schedules a Delivery

Let’s now look at how to evaluate the candidate realizations for an operation.

Evaluating candidate system operation realizations

If there are multiple possible system operation realizations, you need to evaluate them using the dark energy and dark matter forces and pick the one that best resolves the forces. Let’s first look at how to evaluate the two candidates for acceptOrder() operation using the dark energy forces. After that, I’ll describe how to evaluate them using the dark matter forces.

Evaluating the candidates using dark energy forces

Candidate #1, which puts both subdomains in the Kitchen Service, resolves each of the dark energy forces as follows

  • Simple components - Delivery Management is a complex subdomain and Kitchen Management is a complicated subdomain and should ideally be implemented by separate services.
  • Team autonomy - Delivery Management and Kitchen Management are owned by separate teams and should ideally be implemented by separate services.
  • Fast deployment pipeline - Delivery Management is a complex subdomain and Kitchen Management is a complicated subdomain and should ideally have separate deployment pipelines.
  • Support multiple technology stacks - the two subdomains use the same technology stacks and so there’s no issue with putting them in the same service
  • Segregate by characteristics - the two subdomains have similar characteristics and so there’s no issue with putting them in the same service

As you can see, it only partially resolves these forces. Most notably, it doesn’t adequately resolve the Team Autonomy force.

In comparison, candidate #2, which assigns the Delivery Management subdomain to a separate service, resolves the all of dark energy forces.

Let’s now look at these candidate realizations resolve the dark matter forces.

Evaluating the candidates using dark matter forces

In candidate #1, acceptTicket() is a local operation. As a result, this design resolves all of the dark matter forces.

In comparison, in candidate #2, acceptOrder() is a distributed operation. However, it still adequately resolves the dark matter forces:

Selecting the best operation realization

Once you have evaluated the candidate realizations, you need to pick the best one.

Here’s how the two example design candidates compare:

Consequently, candidate #2 is the best overall design because it resolves the all of the dark energy forces and adequately resolves the dark matter forces.

Updating the service architecture

The chosen operation realization is then applied to the architecture, which results in the following changes:

  • Updates the API Gateway - routes acceptTicket() request to the Kitchen Service
  • Updates the existing Kitchen Service:
    • Add the Delivery Management subdomain to this service
    • Add the service operation acceptTicket()
    • Add a TicketAccepted published event
  • Adds a new Delivery Service:
    • Add the Delivery Management subdomain to this service
    • Add an event handler for the TicketAccepted event that schedules a Delivery

Once you’ve designed the operation realization for acceptTicket(), you can then move on to the next operation in the list.

What’s next

The process of designing a service architecture described in this article is a series of operation-level design decisions. As a result, there’s a risk that some decisions will be suboptimal from a global perspective. In the next article, I describe step 4 of the Assemblage architecture design process, which evaluates the service architecture in order to identify areas for improvement.

Need help with accelerating software delivery?

I’m available to help your organization improve agility and competitiveness through better software architecture: training workshops, architecture reviews, etc.

Learn more about how I can help


application architecture   architecting   dark energy and dark matter   assemblage   microservice architecture   service architecture  


Copyright © 2024 Chris Richardson • All rights reserved • Supported by Kong.

About Microservices.io

Microservices.io is brought to you by Chris Richardson. Experienced software architect, author of POJOs in Action, the creator of the original CloudFoundry.com, and the author of Microservices patterns.

ASK CHRIS

?

Got a question about microservices?

Fill in this form. If I can, I'll write a blog post that answers your question.

NEED HELP?

I help organizations improve agility and competitiveness through better software architecture.

Learn more about my consulting engagements, and training workshops.

LEARN about microservices

Chris offers numerous other resources for learning the microservice architecture.

Get the book: Microservices Patterns

Read Chris Richardson's book:

Example microservices applications

Want to see an example? Check out Chris Richardson's example applications. See code

Virtual bootcamp: Distributed data patterns in a microservice architecture

My virtual bootcamp, distributed data patterns in a microservice architecture, is now open for enrollment!

It covers the key distributed data management patterns including Saga, API Composition, and CQRS.

It consists of video lectures, code labs, and a weekly ask-me-anything video conference repeated in multiple timezones.

The regular price is $395/person but use coupon NPXJKULI to sign up for $95 (valid until December 25th, 2024). There are deeper discounts for buying multiple seats.

Learn more

Learn how to create a service template and microservice chassis

Take a look at my Manning LiveProject that teaches you how to develop a service template and microservice chassis.

Signup for the newsletter


BUILD microservices

Ready to start using the microservice architecture?

Consulting services

Engage Chris to create a microservices adoption roadmap and help you define your microservice architecture,


The Eventuate platform

Use the Eventuate.io platform to tackle distributed data management challenges in your microservices architecture.

Eventuate is Chris's latest startup. It makes it easy to use the Saga pattern to manage transactions and the CQRS pattern to implement queries.


Join the microservices google group