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
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 (we want to avoid multi master services). Since we want our service to be autonomous (i.e. be able to make a decision without having to communicate synchronously with other services), we also looked how to avoid 2 way communication (RPC, REST 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 which involves a different view on services, where they’re not autonomous. Instead services 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) 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:
[blockquote author=”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.[/blockquote]
Business capabilities are the essential part of the software we develop. Dan North has the following to say on the subject:
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:
[blockquote author=”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.[/blockquote]
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
- It is form of business and IT alignment ensures that we can maintain Service Autonomy & Encapsulation
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:
[blockquote author=”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.[/blockquote]
To help make this less abstract here’s an example of what a composite UI could look like:
The composition of Service UI partials (each partial contains a part of the complete UI) can happen client side or serverside. One way to think of it is the each service gets to render its UI into a designated DIV in the webshop UI page:
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 Review 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 application visualization decision. All the Review service might output could be <score>4.2</score>. How this is rendered in the UI is up to the styling. From the applications point of view all they 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 once a new requirement for Reviews is introduced it only needs to be implemented inside the ReviewService’s code base. The UI parts for the different application platforms of course needs to be updated to reflect the new requirements and pushed to the applications, but 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 a given service needs to be able to provide its UI partials 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:
In my opinion Composite UI’s is where autonomous services really makes a strong case for rendering content from multiple services without having to couple these services to each other. You get to have good encapsulation services at all levels. The interfaces/contracts we expose reveal very little of the services data and capabilities. Contracts that typically gets exposed are consumer driven IT operations local interfaces (3rd party/legacy integration), events (which typically only suffice with containing what happened and the aggregate id the change was related to), a few externally exposed commands and of course our UI partials. Apart from these there are very few contracts/interface that gets exposed to the eco system.
Update: One of the challenges for 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 problems when one or more services fail to update, but others succeed. 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
If a service is expected to be deployed where ever its data is needed, it brings into question if a service is 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 above, we arrive at the conclusion that 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 likely 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. (put in another way Business capabilities cross application boundaries)
- 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.
The microservices are effectively the implementation details of the logical service.
We focus on and talk about services and the business capabilities/use-cases they support – 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 (e.g. imagine having to change a Microservice responsible for a read model because it’s too slow or contains too few data). 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 grabs messages (e.g. Commands) off a Queue and handles them asynchronously (when that makes sense).
The endpoint could also be a normal Java/.NET/etc. interface, which for IT Operations (e.g.integration gateways) can be used without incurring a remote method call.
The microservices, that make up the Service, owns the individual domain model(s) that cover the functionality they are responsible for, which could e.g. be aggregate write model(s), view-models/projections, reports, CRUD models. Finally it’s also the microservices that publish Events onto a MessageBus, Topics or expose Atom+Pub REST endpoints, so other microservice can subscribe to and consume them.
So how small should a microservice be?
In part 3, based on Pat Hellands “Life Beyond Distributed Transactions ? – An Apostate ‘s Opinion”, we formulated a rule of thumb that says: 1 use-case = 1 transaction = 1 aggregate. This means that for mutating 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 should be responsible for all mutating operations on a single aggregate. If we go smaller, then we can’t guarantee consistency for our aggregate and increase in complexity will get much higher.
Said another way: A microservice is the devision of Services along transactional/consistency boundaries.
On the read side of things, e.g. reports or queries, the smallest microservice that we should create should be responsible for maintaining the read model (e.g. through events published by the write side microservice in CQRS style), performing the query or generating the report. Readers familiar with CQRS will know that read-models/queries/reports always involve more than one aggregate instance and sometimes more than one aggregate type, so it only makes sense to encapsulate this inside a deployable unit of computing, i.e. a microservice.
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 or split a microservice into smaller parts.
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 and NOT also with Services which they also need to be if you want to succeed with autonomous services and microservices. Whats even worse in most origanization 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:
That’s it for this blog post, in the next blog post I hope to look into how integration with legacy and 3rd party applications can be done and what it means for our design.