Part 1 – Microservices: It’s not (only) the size that matters, it’s (also) how you use them
Part 2 – Microservices: It’s not (only) the size that matters, it’s (also) how you use them
Part 3 – Microservices: It’s not (only) the size that matters, it’s (also) how you use them
Part 4 – Microservices: It’s not (only) the size that matters, it’s (also) how you use them
Part 6 – Service vs Components vs Microservices
Text updated the 27th of June 2021
First of all, sorry to those who’ve been waiting for part 5. My schedule has been too busy to find focused time to write part 5 before now 😳 .
In part 4 we looked at building our services around functional areas or business capabilities/bounded-contexts.
We discussed that the business data and logic pertaining to a business capability must be encapsulated inside a service to ensure single source of truth for the data.
This also means that other services aren’t allowed to own the same data that another service owns because we want to avoid multi master services.
Since we want our service to be autonomous, i.e. the service should able to make a decision without having to communicate synchronously with other services, we also looked how to avoid 2 way communication (RPC, REST, GraphQL or Request/Response) between services.
The options we looked at were Composite UI’s and Data duplication over Events.
We also briefly discussed a 3rd option that involved a different view on services, where they’re not autonomous and services instead expose intentional interfaces and coordinate updates/reads between several System of Records (SoR) that them selves are autonomous. I believe that organizations with many large legacy systems (and most likely multi master Systems of Records) should look into the possibilities of the 3rd option, as I believe it may create less friction than trying to develop new autonomous services that are well aligned with business capabilities.
In part 5 I will continue discussing SOA and Microservices in the light of autonomous services.
Business Capabilities and Services
In part 4 I suggested building our services around functional areas or business capabilities/bounded-contexts.
I would like to tighten up that statement and rephrase to:
We should align our services with business capabilities.
Why? In http://bill-poole.blogspot.dk/2008/07/business-capabilities.html Bill Poole explains why he thinks using Business Capabilities for Service alignment is the right way to go:
Bill Poole:… a business capability is something that an organisation does that contributes in some way towards the overall function performed by the organisation.
The advantage of business capabilities is their remarkable level of stability. If we take a typical insurance organisation, it will likely have sales, marketing, policy administration, claims management, risk assessment, billing, payments, customer service, human resource management, rate management, document management, channel management, commissions management, compliance, IT support and human task management capabilities. In fact, any insurance organisation will very likely have many of these capabilities.
Business capabilities are the essential part of the software we develop. Dan North has the following to say on the subject:
Dan North: Business Capability is the asset
Finally in http://www.udidahan.com/2010/11/15/the-known-unknowns-of-soa/ Udi Dahan states why he thinks Services should be autonomous and the technical authority for a specific business capability:
Udi Dahan:…synchronous producer/consumer implies a model where services are not able to fulfill their objectives without calling other services. In order for us to achieve the IT/Business alignment promised by SOA, we need services which are autonomous, ie. able to fulfill their objectives without that kind of external help.
A service is the technical authority for a specific business capability.
Any piece of data or rule must be owned by only one service.
What this means is that even when services are publishing and subscribing to each other’s events, we always know what the authoritative source of truth is for every piece of data and rule.
I have summed the above statements into the following rule:
A Service is
- The technical authority for a given business capability
- It is the owner of all the data and business rules that support this business capability – everywhere
- It forms a single source of truth for that capability
The consequence of this defintion is that:
A service needs to be deployed and available everywhere its data/logic is needed.
Thinking about it that makes a lot of sense. In http://www.udidahan.com/2010/11/15/the-known-unknowns-of-soa/ Udi Dahan explains why:
Udi Dahan: …when looking at services from the lense of business capabilities, what we see is that many user interfaces present information belonging to different capabilities – a product’s price alongside whether or not it’s in stock. In order for us to comply with the above definition of services, this leads us to an understanding that such user interfaces are actually a mashup – with each service having the fragment of the UI dealing with its particular data.
Ultimately, process boundaries like web apps, back-end, batch-processing are very poor indicators of service boundaries. We’d expect to see multiple business capabilities manifested in each of those processes.
If we examine the domain of banking we can see that it provides multiple UI’s such as customer facing Mobile phone app, full Web applications and back-office applications. Each of these UI’s will present data from many underlying business capabilities:
Multiple bank UI’s
To help make this less abstract here’s an example of what a concrete composite UI could look like.
Each red box represents a UI widget delivered by a Service. Each widgets form a part of the complete UI.
The only data shared between the UI and the Service UI widgets is the id of the Product being displayed (this id can be shared as a cookie, url parameter, UI event, page shared variable, etc.)
Composite UI example Amazon
Layout wise the page where the UI widgets are placed is owned by the application. The application doesn’t know how the widgets are implemented or how they fetch data. As mentioned the only contract is id of product being displayed
The composition of Service UI widgets can happen client side or server side. One way to think of it is the each service gets to render its UI into a designated DIV in the webshop UI page.
Composite UI – widgets on a page
Here’s an example of how loading a page can be coordinated on the front end.
Clarification: If the UI composition is performed client side, then the communication between a client side UI widget and its server side counterpart API will 99,9% of the time be in the form two way communication (typically async Ajax calls against a REST interface). Using events between the UI and the backend is typically only used for notifications.
The advantage of Composite UI’s is that the application, here the WebShop, doesn’t need to know any details about each of the services that provide UI partial to the page. The fact that a Review is a combination of Score, Number of Reviews and Number of Likes is completely encapsulates in the Review Service. The fact that the review score is rendered as stars, instead of a number, is a concrete frontend UI visualization decision.
The Review service might only output a “<score>4.2</score>”. How this is rendered in the UI is up to the styling.
From the applications point of view all the UI widgets share is the Page Context (e.g. contained in a page variable, cookie value, shared using an Event that each UI composite listens to), the styling contract (e.g. CSS based) and the fact that the ReviewService’s UI should be rendered into a DIV with id “Book:Review”.
The advantage is that once a new requirement for Reviews is introduced it only needs to be implemented inside the ReviewService’s code base as well as ReviewService owned UI widgets. Nothing inside the applications needs to change. The change is completely local to the ReviewService.
Another advantage of using composite UI’s is that other services rarely needs to subscribe to events from other service just to build up caches/replicas of other services data. This problem is solved at the composition level.
The downside is that all Services must be able to provide its UI widgets to ALL applications on potentially MANY different platforms such as an iOS app, back office .NET app, a Java based Webshop, etc. as exemplified below where multiple services are part of rendering/printing an invoice in a composite way:
Composite UI example Invoice
In my opinion Composite UI’s work really well with autonomous services as it extends the Service encapsulation all the way to the UI.
Update: One of the challenges with composite UI’s is when it comes to updates (e.g. submitting a form across multiple services), because we here run into the classical problems with updating data across transactional boundaries without having XA/2PC transactions to help solve the problems that occur when one or more services fail to update where as other succeed. Often times this can be solves by using page transitions, but not always.
You also have to take into account that the browser is a less reliable platform, due too browser hangs or the user closing the page, than a server side implementation of the same orchestration.
If you’re further interested in Composite UI’s I can recommend reading http://www.udidahan.com/2012/06/23/ui-composition-techniques-for-correct-service-boundaries/, http://www.udidahan.com/2014/07/30/service-oriented-composition-with-video/ and this video by Udi Dahan.
Service deployment model
Since a service is expected to be deployed wherever its data is needed, this makes service deployment the next issue we need to look a.
This begs the question: is a service a physical construction/boundary?
According to Philippe Kruchten’s 4+1 view of architecture the logical view and the physical view (or deployment view) should be independent of each other (i.e. they shouldn’t map 1 to 1).
If we combine this with our service defintion, we arrive at the conclusion:
A Service must be a logical construction/boundary.
I’ve summed this up below into the following definition:
- Systems/Applications are (runtime) process boundaries – A Process boundary is a physical boundary (simplest example is a single .exe og .war deployed unit)
- A Service is a logical boundary, not a physical one. You could choose to deploy a Service as a single Physical (runtime) process, but that’s just ONE way of deploying a service (as we will see later in this blog post) and not necessarily the best way to do it.
- Therefore Process/application/system boundaries and service boundaries are often not the same
To support application composition across multiple service, each service should be able have the following deployment options:
- Many services can be deployed into the same system/application/process
- Parts of a service can be deployed into applications on many different platforms (Java, .NET, iOS, Android, Web, etc.) – e.g. the UI part of your service could be deployed/packaged up into a Web application, an iOS application, an Android Application (with each e.g. being a separate implementation package for the individual platform, but they all still belong to the same logical service)
- An example: The price of product can be displayed both on the web shop, on the backoffice application, on the iOS commerce application, etc.
- Service deployment is not restricted to tiers – the same service can be deployed to multiple tiers / multiple applications
- Part of service A and B can be deployed to the Web tier
- And another part of Service A and B can be deployed to the backend/app-service tier of the same application
- Many services can be deployed to the same server
- Multiple services’ UI’s can be packaged/loaded into the same page (service mashup)
What is a Service made up of?
If a Service is a logical construction/boundary and it can be deployed multiple places, what is a service made up of?
In my opinion autonomous services, as described here, are made up of internal autonomous components or microservices that in total work to support the Service’s, and thereby the business capability it aligns with, various functionalities/use-cases.
Microservices are effectively the implementation details of a logical service.
Service vs Microservices
We focus on and talk about services and the business capabilities/use-cases they support, which means that we’re not overly concerned with their implementation details, i.e. their microservices (or autonomous components).
This is a good thing, because Microservices are much less stable than Services. Focusing on the service and not the implementation details makes it much easier to rewrite the microservices (as long as their contracts are stable) or supplement (in case we need another version of the microservice running in the environment).
Each microservice can have one or more endpoints with which applications/gateways/etc. can interact with them. An endpoint could be e.g. be an HTTP endpoint that e.g. returns UI in the form of HTML, a REST endpoint performs a Query or handles a Command. It could also be a Message Queue endpoint where the microservice takes messages (e.g. Commands) off a Queue and handles them asynchronously.
The endpoint could also be a normal Java/.NET/etc. interfaces, which for IT Operations style integration can called directly without incurring the cost of remote calls.
So how small should a microservice be?
In part 3, based on Pat Hellands “Life Beyond Distributed Transactions – An Apostate’s Opinion” (original article from 2007) / “Life Beyond Distributed Transactions – An Apostate’s Opinion” (updated and abbreviated version from 2016), we formulated a rule of thumb that says:
1 use-case = 1 transaction = 1 aggregate.
This means that for every data-changing (write/update) operations (i.e. an operation that has side effects, such as changing business data) they should in general only affect one aggregate in order to ensure scalability and consistency without resorting to distributed transactions.
This means that the smallest microservice that we should create must be responsible for all data changing operations on a single aggregate. If we go smaller, then we can’t guarantee consistency for our aggregate and complexity will increase.
Said another way: A microservice is the devision of Services along transactional/consistency boundaries.
On the read side, e.g. reports or queries, the smallest microservice that we should create must be responsible for maintaining the given read model or report (e.g. through events published by the write side microservice in CQRS style).
This doesn’t mean a microservice should only be concerned with only one of the concerns above. Depending on performance, scalability, consistency, availability requirements we could choose to bundle more concepts into a single microservices (e.g. all writes/updates and queries related to a one or more Aggregate types) or split a microservice into smaller parts (separate writes/updates from reads for a given Aggregate type into separate microservices).
Logical service components
Also, there’s nothing here that mandates or requires that a Service absolutely must use events to communicate between its internal parts (Microservices). It’s absolutely possible and reasonable to review alternative storage platforms that can handle distribution of data, e.g. NuoDB.
There’s also NOTHING that says that Microservices MUST/SHOULD be deployed in their own process. In my view Microservices are logically deployable units. This means they CAN be deployed individually, but it should only be done when it makes sense!
Individually deployed units of computing entails costs for serialization, deserialization, security, communication, maintenance, configuration, deployment, monitoring, etc. So only take this expense when you have a (typically) non-functional requirement that mandates/requires processing units to be deployed individually.
Finally we haven’t covered what this way of working with autonomous services and microservices means for the organization in the light of Conways law, which states that:
organizations which design systems … are constrained to produce designs which are copies of the communication structures of these organizations
Said another way: The way you organize your teams has a direct influence on what you architecture will look like:
- If you split a project between 3 teams, you will get 3 services.
- If you split a compiler between 5 teams you will get a 5 step compiler.
One of the challenges with most organizations is that Teams are typically aligned with Applications.
We also need to have a Team that’s aligned with one or more Services/Business-Capabilities if we want to succeed with autonomous services and microservices.
Whats even worse in most organization is that teams are typically only aligned with Projects. Every new project is setup with a new team.
Jay Kreps puts this problem into perspective:
Software is mostly human capital (in people’s heads): losing the team is usually worse than losing the code
That’s it for this blog post, in the next blog post I will investigate Service vs Components vs Microservices further.