Microservices (and decentralised data management)

https://bit.ly/2NAZPfm

Copyright 2014-19 Graham Berrisford. One of more than 300 papers at http://avancier.website. Last updated 15/05/2019 22:48

 

In 2014, Martin Fowler (a brilliant writer) defined microservices in terms of nine characteristics..

·         Componentization

·         Decentralized data management

·         Decentralized governance

·         Organisation around “Business Capabilities”

·         Smart endpoints and dumb pipes

·         Design for failure and "You build, you run it"

·         Products not Projects

·         Infrastructure Automation

·         Evolutionary Design

 

Before we discuss these, there are many general issues to address.

This paper was initially written to support the architect training courses advertised on our web site.

It has been extended to answer questions posted in Linkedin discussions and is now downloaded more than 10,000 times a year.

It draws from discussions with software architects about difficulties they are having with microservices.

And from discussions with EA teams about the need govern “agile” enterprise application development.

 

To quote from comments and discussion in Linkedin

·         “At last, a post on architecture with some real content.”

·         “This is worth reading again and again and sticking in the diary to read again.”

·         “Watching a presentation on Microservice Architecture I was astounded by the lack of perception [of the points in this paper].

·         “Your analysis of loose coupling is a poetry that everybody getting into system integration should read several times until it is really understood.”

Contents

Motivations and tradeoffs. 2

What is a microservice?. 4

A brief aside on terminology torture. 5

Modular design trade offs. 6

Microservices architecture. 7

Microservices as server-side application components. 8

Hierarchically-layered client-server architecture. 8

Dividing the server-side application. 9

Microservice coding options. 11

Issues raised by microservices. 14

CAP theorem, ACID and BASE.. 15

Lessons from CBD history. 17

Particular conclusions and remarks. 18

General conclusions and remarks. 19

Footnotes. 21

 

Motivations and tradeoffs

In essence, the idea behind microservices is that small systems are easier to build and maintain than large ones.

But people have associated that one simple idea with many and various other ideas.

An architect recently complained his CIO using the term as meaninglessly as the term “SOA” has been used for many years.

 

This paper was written to:

·         make sense of the term “microservice

·         steer people away from badly-designed, hard-to-maintain and poorly-performing microservices

·         encourage enterprise and solution architects to take responsibility for governing software architects.

 

There is much advice to be found on microservices; and some of it is good e.g. here.

But beware advice from technology vendors, and know that microservices are not a panacea, since all is tradeoffs.

Division into microservices can lead to the 4Ds: Duplications, Distintegrities, Delays, and Data analysis difficulties.

 

What is the motivation?

The term micro implies subdividing something that could have been macro.

Of four motivations that have been advanced for microservices, the last two are interesting here.

 

-1- To maintain data stores that are already distributed and separately managed.

This is not interesting here, because it describes the application portfolio most enterprises already have.

And integrating discrete applications is a system integration task as it ever has been.

 

-2- To separate what must be coded using different technologies.

This is not interesting here, because such application components are naturally discrete.

And it seems advisable to not to mix technologies unless you have to.

 

-3- To enable very high throughput (by processing transactions in parallel components).

Exceptional requirements require exceptional designs, for which a price must be paid.

However, few enterprise applications have a throughput high enough to require division to parallel microservices.

Solid state drives and in-memory data storage can handle remarkably high transaction volumes (e.g. 20,000 transactions per second)

 

-4- To facilitate agile development.

This seems the most common motivation, and the one Fowler clearly had in mind.

The aim is to help a smallish team develop and deploy a microservice quickly.

Then maintain and extend it with minimal disruption to what other teams are doing.

 

What are the trade offs?

Microservices are not new, and not more sophisticated than other design patterns.

The pattern can be used well, and can be used very badly; all is trade offs.

 

Agile tradeoffs

It is important to recognise that agile development principles can be in conflict.

The classic tradeoffs in agile development are these.

 

Agile principle

Contrary agile principle

Keep the system simple

Decouple (logically coupled) subsystems

You ain’t gonna need it

Design for future flexibility

 

Software sociology

As Conway pointed out in 1968, team structures tend to reflect software architecture structures and vice-versa.

Still, software sociology – assigning different application subsystems to different teams - cannot remove dependencies between subsystems.

 

The impacts of downsizing

The term micro implies subdividing something that could have been macro.

Still, the classic design tradeoffs apply, and most obviously, excessive modularisation leads to excessive messaging.

 

Subsystem

Inter-subsystem

Size

Processes

Messaging

Coupling

Macro subsystems

Larger

Longer

Less

Looser

Micro subsystems

Smaller

Shorter

More

Tighter

 

Decentralising data management

Often (if not by definition) a microservices architecture divides what could be one coherent persistent data structure.

However, dividing a coherent database schema into smaller schemas doesn’t remove relationships between data entities.

It moves the maintenance of inter-entity relationships from the database into the messaging system, at some costs. 

 

Dividing a system into smaller/simpler modules leads to larger/more complex messaging.

Division into microservices can lead to the 4Ds:

·         Duplications, caching of data from one data store in another.

·         Disintegrities, and the extra complexity of compensating transactions to restore integrity.

·         Delays, processes are slowed by the need for inter-application messaging.

·         Data analysis difficulties; where a report requires data from several data stores.

 

Other points

OK, domain-oriented modularity has its place.

But some gurus have encouraged the writing of over complex OO code on app servers.

Transaction scripts and subroutines thereof can be fine; and some code is better on data servers.

 

OK, message brokers and workflow engines can be useful.

But business rules are not best placed in the middleware.

Rules engines are niche tools; some rules are best at the user interface; some are best as near to the stored data as possible.

Some rules are best configurable, but you don’t want mission-critical business rules in the hands of users.

 

In short, there is no silver bullet, you can’t have everything.

What is a microservice?

In short, microservices is a design pattern for the modular design of enterprise applications.

 

Modular design before microservices

Abstract away from the technologies and enterprise application design is a story of modular design.

In the 1960s, Larry Constantine promoted strong cohesion within a module and low coupling between modules.

By the 1970s, several modular design principles were widely recognised.

 

Design principles

Meaning that

Cohesion

Modules encapsulate cohesive data structures and algorithms

Loose coupling

Modules are encapsulated by and communicate via interfaces

Composability

Modules can be invoked and orchestrated by higher processes

Reusability

Modules are designed for use by different clients

Maintainability

Modules are maintained in several versions to enable incremental change

 

Since 1970s, there have been many modular design fashions and techniques.

Read SOA and the Bezos mandate for notes on:

·         RPC: Distributed programming

·         DO: Distributed Objects

·         CBD: Component-Based Design

·         SOA: Service-Oriented Architecture

·         REST: Representational State Transfer

·         SOAP versus REST

·         The Bezos mandate

 

Microservices

Briefly, a true microservice is much as Fowler defined in 2014:

·         a micro application, encapsulated behind an API

·         a discretely deployable subdivision of what could be a monolithic application (a macroservice?)

·         it encapsulates one partition of what could be a monolithic data structure, and so “decentralises data management”.

 

Is everything that realises an API a microservice?

No. Every microservice should have an API, but not every API encapsulates a microservice

To use the term for every module behind an API is to make the term meaningless and valueless.

A small application is just a small application – nothing new or special about that.

A silo application is just a silo application – nothing new or special about that.

Small and silo applications are readily developed using agile principles – because they are naturally easier to write and to change.

And there is nothing new of special about wrapping up a small or silo application behind an API.

 

How do microservices relate to Bounded Contexts in Domain-Driven Design?

A bounded context has its own ubiquitous language and its own architecture.

However, two bounded contexts can share concepts (e.g. customer, or product).

That describes the typical enterprise application portfolio since the 1970s.

The EA nightmare has been silo apps (separately developed and maintained) that store overlapping data structures.

 

As I see them, microservices are smaller than bounded contexts; they further decentralise data management.

They apply Conway’s law (in reverse) to a large bounded context.

What makes a microservice "micro" is division of one data structure into agile development-sized chunks

A microservice maintains a subset (perhaps as small as an aggregate entity) of one coherent and cohesive persistent data structure.

If there is no dependence between microservices, then they are discrete silo applications.

 

Should microservices should be isolated and autonomous?

This ranges from questionable to impossible where data integrity is needed.

One writer says a microservice is "a self-contained and independent unit with a well-defined scope and responsibility".

But this definition can fit everything from a single Java class to an entire CRM system.

Moreover, you could define a good API the same way.

And the notion that microservices are independent of each other is misleading.

 

Will dividing a monolithic application into microservices affect the services offered to clients?

It can do if microservices are decoupled too far.

 

Are microservices are “gatekeepers” to data? 

Yes in that all updates should be applied by the microservice.

However, the data can be accessed by other software for analysis and reporting.

A brief aside on terminology torture

In the babble that enterprise and software architecture discussion has become, terms are used loosely and often abused.

Some use the terms system, component, process and service interchangeably.

Some use them with different (and sometimes contradictory) meanings.

 

 

In EA standards from The Open Group

In Fowler’s writings for programmers

Component

A structural building block that offers

multiple services (a service portfolio) to its clients

A software unit (component) that is

independently replaceable and upgradeable.

Process

A behavior composed of steps

in a sequence that leads to a result.

An instance of a component executed on a computer.

It contains executable code and current state data.

(Some OS allow it to contain multiple concurrent threads of execution.)

Service

The external or  declarative  view of behaviour that

encapsulates one or more processes.

An out-of-process component,

typically reached by a web service request or RPC.

 

Remember Martin Fowler’s process and service correspond to application components in TOGAF and ArchiMate.

And TOGAF’s Function is a logical component that groups behaviors by some cohesion criterion other than in sequence (which is a process).

 

It is commonly said that “enterprise architecture views the enterprise as a system, or system of systems”.

But there are profound misunderstandings of what this means.

To call every problem, situation or business “a system” is unhelpful.

It is important to distinguish:

·         a social network in which people communicate

·         a social system in which people realise role and rules.

 

An enterprise is one social network that realises many social and technological systems.

Those systems may overlap, duplicate or even be in conflict.

Enterprise architecture is about applying change control to large-scale, generational, system change.

 

In short, beware the terminology clashes that are the curse of every writer in this field.

Using EA terminology, a microservices architecture would be better called a microcomponent architecture.

The services offered by the wider monolithic application should remain unaffected by division into microservices, but they might be affected.

Modular design trade offs

It is important to recognise design principles and patterns are not mandatory goals; they are only means to some ends.

Since the 1960s, software designers have debated now best to scope a module, separate modules and integrate modules.

Always, one has to consider what trade offs have to be made between requirements.

For example, there are trade offs between flexibility and simplicity, scalability and integrity.

 

Of course, it is quicker to design and build a small subsystem than a large system.

But the smaller and simpler each sub system, the less useful it is, and the more complex the integration between subsystems.

This table lists my universal modularisation trade offs.

 

Agile developers’ dream

Enterprise architects’ nightmare

Smaller, simpler modules

Larger, more complex module dependency & integration

Module isolation/autonomy

Duplication between modules and/or difficulties integrating them

Data store isolation

Data disintegrity and difficulties analysing/reporting data

 

Loose-coupling is often promoted as though it is always a good thing, but this is not true.

Designing systems or components to be “isolated” and/or “autonomous” can lead to disintegration of data and processes.

 

How does decentralisation affect EA?

EA was a response to silo system proliferation.

Themes in EA literature include data and process quality, data and process sharing, data and process integration.

EAs are wary of excessive decoupling of software components across a network and/or via middleware.

Since it can create needless disintegrities and complexities, not to mention needless costs and performance issues.

Excessive decoupling can hinder how customers or employees perform business processes.

Microservices architecture

A microservices architecture divides an application into modules or components - confusingly called micro services.

It might be seen as an application of modular design, OO design, CBD or SOA principles.

Microservices might be distributed and integrated over a network, but this is not essential.

 

Martin Fowler (2014) defined microservices in terms of nine characteristics, most of which amount to principles.

The first six about are how best to scope a module, separate modules and integrate modules.

·         Componentization (discussed in this paper)

·         Decentralized data management (discussed in this paper)

·         Decentralized governance (click on the link to read more)

·         Organisation around “Business Capabilities” (click on the link to read more)

·         Smart endpoints and dumb pipes (click on the link to read more)

·         Design for failure and "You build, you run it" (click on the link to read more)

 

Fowler’s last three principles are drawn from the wider agile development movement, not discussed here.

·         Products not Projects

·         Infrastructure Automation

·         Evolutionary Design

Microservices as server-side application components

c1980, an EA vision was a central database, supporting one application that served all the enterprise’s business functions.

That vision became unachievable as enterprises deployed ever more applications, which were increasingly distributed.

The vision evolved: EA divided the enterprise into “segments”; and the centralised data vision was recast as “single-version-of –the-truth”.

EA now involves “master data management” within each segment of the enterprise, and sometimes across segments.

EA expects each enterprise segment to be supported and enabled by many applications; some in-prem and some in-cloud.

 

Much as an enterprise may be divided into segments, an enterprise application may be divided into application components.

A typical client-server enterprise application processes successive transactions and is divided into three layers.

Microservices divide the server-side of what could be one large application into components that are supposedly autonomous.

By “decentralising data management” Fowler implies dividing what could be one persistent data structure into parts.

The idea is that each microservice encapsulates the data it needs; it does not directly access the data maintained by any other microservice.

Each is an application component that can perform one or many transactions or partial transactions.

 

Size matters!

You might see microservices as a scaling down of the “Bezos mandate” and/or Service-Oriented Architecture.

In other words, as extending the principle that components are distributed across a network and communicate via APIs.

However, microservices do not have to be distributed across a network.

Other modularisation strategies are no less service-oriented; and SOA does not say how to divide an application into modules.

 

You might better view microservices as a scaling up of OO design principles.

In other words, as a response to complaints that the granularity of “objects” is too small for architecture-level design.

Hierarchically-layered client-server architecture

An enterprise application enables or supports one or more business roles and processes.

It encapsulates some business rules, business knowledge or business capability.

A few enterprise applications are purely algorithmic, or need very little stored data.

But the majority maintain, or at least need access to, a substantial business data store.

 

Dividing an application into three layers dates back to the 1970s - the days of COBOL and pre-relational databases.

Fowler says an enterprise application is typically composed of three layers, as shown below.

 

Client-side user interface

Typically HTML pages and javascript running in a browser on the user's machine

Server-side application

Controllers: handle requests/commands from the client

Views: populate views/pages sent to the client.

Models: retrieve and update data, applying business rules

Data store

A persistent data structure maintained using a database management system.

Typically a relational DBMS, but there are many data store varieties.

 

The layers form a client-server hierarchy, meaning each layer requests services from the layer below.

The requirements for most enterprise applications may be seen from the client side and the server side.

 

Client side requirements

On the client side, the users require a user interface, usually divided into views.

The views are designed to what the user want to do in different use cases/sessions.

And within each use/case session, the user enters queries and commands the server-side application must respond to.

 

Server side requirements

On the server side, the back-end application must respond to the queries and commands.

In short term memory, it must remember what the user is currently doing in one session.

In long term memory, it must remember more persistent data – e.g. about products, customers, and all the orders they have placed.

 

Despite the OOPers mantra that business rules belong in the middle layer, some regard the data layer as the best place for data-centric business rules.

Middleware may be used to connect layers, but Fowler says "smart end points, dumb pipes", meaning don't put business rules in middleware.

Dividing the server-side application

Fowler says the server-side application is typically a single executable.

One large application serves many requests/commands from clients.

Moreover, it is seen as a single software development unit.

So changes to requests/commands involve building and deploying a new version of the whole server-side application.

 

Microservices are subdivisions of the server-side application layer.

Each microservice receives a subset of the service requests that clients make of the whole application.

The ideal is that each of these application components can be built and tested in parallel.

 

Client-side user interface

Screens / Pages

Server-side application

Customers, Agents, Sales

Reservations, Rooms, Hotels

 

How to divide one application into microservices? Fowler suggests:

·         “Organisation around Business Capabilities” according to Domain-Driven Design (see footnote 1).

·          “Decentralized data management”, which is discussed in this paper.

 

“At a first approximation, we can observe that microservices map to runtime processes, but that is only a first approximation.” Fowler

 

Client components (of any kind) do not have to be invoked a microservice via a message broker.

Microservices can be invoked using web service requests, the OData protocol, or whatever suits.

The closer microservices are adjoined in location and time, the more likely synchronous request-reply invocation will be appropriate.

 

Dividing the data store

Microservices imply not only dividing the application layer processing of what could be one cohesive data structure.

But also partitioning that data structure to match the division of the application layer into microservices.

 

“A microservice may consist of multiple processes that will always be developed and deployed together, such as an application process and a database.” Fowler

In other words, an application layer microservice cannot work without its data store.

 

To keep thing simple, let us minimise the need for a data abstraction layer between application and data layers.

In other words, the class diagram of the application layer and the data model of the data layer are variants of one logical domain model, and divided the same way.

 

Client-side user interface

Screens / Pages

Server-side application

Customers, Agents, Sales

Reservations, Rooms, Hotels

Data store

Customers, Agents, Sales

Reservations, Rooms, Hotels

 

If you decouple the microservices’ data structures, and store them separately, then you need a cross-reference between them.

At least one entity type is duplicated in adjacent microservices' data stores, with a common primary key, but different, attributes and relationships.

That is an old and generally applicable idea; it can be applied however you choose to divide work between horizontal layers.

 

On division of work between horizontal layers

Regardless of division into vertical partitions, an idea that Fowler seems to presume is that all business logic is in the application layer.

However, a data model captures business-specific terms, concepts, data types, data derivation and data integrity rules.

And coding data-centric rules next to the data is a rational design strategy.

So it is common to find data-centric business rules in the data layer.

Especially basic consistency/integrity rules such as: “no row can be inserted into the Sale table unless there is a corresponding row in the Customer table”.

And perhaps: no row can be removed from the Customer table if that Customer has Sales with future-dated Reservations.

Microservice coding options

Macro applications might be seen as the enemy here, but a macro application is always modularised one way or another.

As Fowler says, most business applications handle a series of discrete business transactions (event-triggered processes).

And they maintain a cohesive data resource – describable as data entities related to each other in a network structure.

 

The database of a microservice is a discrete division of what would be a wider database.

That is, the database of an application not divided into microservices (aka application components).

This table illustrates the mapping of:

·         event-triggered business processes against

·         entities in the data model of that wider database.

 

Entities

Customer

Sale

Sale item

Product type

Events

Register customer

Create

 

 

Place order

Read

Create

Create

Update

Complete sale

 

Update

 

 

Launch product

 

 

Create

Recall product

Read

Read

Update

Update

 

Event-oriented or entity-oriented programming?

In  short, each microservice may be coded either as:

·         a set of event-triggered transaction scripts, or else as

·         an aggregate entity in which the root entity manages all the transactions.

 

The concept of the aggregate entity probably implies the use of Fowler’s Rich Domain pattern and/or Evan’s Domain-Driven Design.

There seems no clear reasons to insist microservices should use those patterns.

In 2002, Martin Fowler observed that Domain-Driven Design is difficult to learn and best reserved for complex systems with “rich domain models”.

And in January 2017, Wikipedia said: “Microsoft recommends that [domain-driven design] be applied only to complex domains.” (where I guess rich implies substantial use of inheritance).

 

A transaction script is an event-triggered procedures.

At a lower level, transactions can share/reuse shorter subroutines, which may be entity or object-oriented.

At a higher level, a business transaction that crosses between microservices is coded as a longer procedure (aka saga) that coordinates micro-service-level transactions.

Fowler suggested transaction scripts are simpler for 90% of business applications.

 

So, further discussion below starts with the transaction script pattern, and moves on to the aggregate entity pattern.

Event-oriented modularisation

You can modularise an application into one module for each event-triggered process to be completed.

That was the expectation in using structured analysis and design methods in the 1980s

Duplication is removed by factoring out common sub-routines (done well, this gives the leanest design).

 

Procedural microservice (handles 1 whole transaction, has access to the whole data structure)

In his book on enterprise application design patterns, Martin Fowler wrote thus.

“Most business applications can be thought of as a series of [client-server] transactions.

[Some are] as simple as displaying information in the database.

[Others] involve many steps of validations and calculations.

A Transaction Script organizes all this logic primarily as a single procedure, making calls directly to the database or through a thin database wrapper.

Each transaction will have its own Transaction Script, although common subtasks can be broken into subprocedures.” Fowler

 

A suggestion for agile development, divide the data model into aggregates, each with a kernel entity (as several examples discussed in our architects course)

Give each programmer the transactions that start in one aggregate - at the kernel/root entity or other entity in the aggregate.

Where few transactions span more than one aggregate, the programmers mostly work autonomously – their code and tests do not overlap.

Where many transactions span aggregates, dividing the code and data structure as per entity-oriented microservices (below) is problematic.

 

As I recall from decades ago, the common subtasks tend to be subroutines that access one entity, or a few entities.

You can draw a simple hierarchical dependency diagram to track inter-dependencies between Transaction Scripts and shared subroutines.

 

Perhaps start with Transaction scripts, and refactor into a Domain-Driven Design only when complexity of the domain demands it?

Entity-oriented modularisation

Fowler proposes the design of microservices should decentralise data management and governance.

This means dividing a wider data model into narrower data structures.

 

Fine-grained entities

You could modularise an application into one module for each normalised data entity.

The coordinated these modules to complete an event-triggered process by

·         Choreography: entity modules call each other to complete the process

·         Orchestration: the process calls the requisite entity modules

Or a mix of both the above, using the GRASP pattern.

 

However modules that encapsulate normalised data entities are too small to be deployed separately as microservices.

Partly because entity-level objects are so small that coordinating them in higher level processes is inefficient.

Partly because it leads to issues addressed by Fowler’s first rule of distributed objects “Don’t distribute your objects!”

 

Aggregate entities

A microservice usually encapsulates a data structure considerably larger than a normalised data entity.

It is reasonably here to employ the idea of “aggregate entities” that appears in Evans’ Domain-Driven Design

 

There are recognised techniques for dividing an OO domain model or data model.

E.g. you may identify aggregate entities by applying cluster analysis (the north-west corner method) to the entity-event table above.

Having divided an application into application components thus, you must define each aggregate entity separately.

This involves defining the several transactions that access the data in that aggregate entity.

 

A true microservice handles a cluster of aggregate-entity level transactions.

It encapsulates a discrete group of data entities - an aggregate entity or more.

Ideally, the data structure is sufficiently wide that a microservice can complete many (most?) clien-server requests on its own.

 

Ideally, the data server and app server layers of the application are partitioned in the same way.

Otherwise, the data abstraction layer between them will be complex, and undermine the hoped-for benefits of microservices.

 

What if one business transaction needs to access the data encapsulated by more than one microservice?

The business transaction can be completed by coordinating microservices – whether by orchestration or choreography.

Or else, you can duplicate some data between data stores, so that each microservices has all the data it needs.

This leads to the complexity of adding processes to align data stores after a transaction – asynchronously - as best they can.

 

A pseudo microservice handles a cluster of whole transactions that have access to the wider data structure.

Again: a suggestion for agile development, divide the data model into aggregates, each with a kernel entity (as several examples discussed in our architects course)

Give each programmer the transactions that start in one aggregate - at the kernel/root entity or other entity in the aggregate.

Where few transactions span more than one aggregate, the programmers mostly work autonomously – their code and tests do not overlap.

Where many transactions span aggregates, dividing the code and data structure as per entity-oriented microservices (below) is problematic.

 

A pseudo microservice packages transactions as suggested above.

The package resembles a true microservice in scope, but does not completely encapsulate the data.

It can be managed as a separately deployable application layer component, accepting that data management remains centralised.

Where transactions span more than one aggregate entity, design options include:

·         allow pseudo microservices to compete for the same data (limits scalability).

·         duplicate (by caching) data needed by several microservices (increases disintegrity and complexity).

Issues raised by microservices

Decoupling is an important tool of design; but it means many things, and can be overdone.

There are many ways to decouple application components: physical decoupling using network and/or messaging technologies is not logical decoupling.

There are many ways to integrate application components: exposing APIs over an IP network is only one way.

Read SOA and the Bezos mandate for discussion of component granularity, network use and middleware use.

 

For some, loose-coupling via messaging has become a mantra, but there are always trade offs, as this table shows.

 

Couple via synchronous request/reply for

Decouple via asynchronous messages/events for

Speed

Simplicity

Consistency

Scale (very high volume and/or throughput)

Adaptability/Manageability (of smaller distributed components)

Availability (of smaller units of work) and Partition Tolerance

 

Things to think about include:

·         Physical decoupling makes logical coupling more complex.

·         Naïve architecture guidance - can mandate decoupling, asynchronicity and scalability where not needed

·         Response time – where one transaction requires microservices that communicate via network and/or messaging.

·         Availability – where synchronous access is needed to partitioned/distributed data stores.

·         Scope/complexity creep – where microservices are increasingly hooked together.

·         Business data and process integrity – where BASE replaces ACID.

·         Application maintenance – where multiple design patterns and technologies are used.

·         Best practice use of CQRS/Event Sourcing.

CAP theorem, ACID and BASE

We are talking about the server-side of an enterprise application.
A remote client sends a service request to a point of entry on the server-side.
That service request is a business transaction, a command or query, definable in a declarative service contract.
The service contract defines I/O data with reference to one or more persistent data entities.

 

Suppose we have modularised a macro application into several true (entity-oriented) microservices.

Ideally, most business transactions don’t stray beyond one microservice.

But what about those business transactions that do involve two or more distributed microservices?

 

CAP theorem

The famous CAP theorem says you can’t have all three of Consistency (integrity), Availability and Partition (broken connection) tolerance.

It doesn’t mention a fourth thing you want from a design – Simplicity.

The disintegrity that results from dividing a cohesive data model tends to increase the complexity of a system.

Allowing data to become inconsistent, then restoring integrity by compensating transactions, is more complex both for programmers and for a business.

 

ACID versus BASE

This table contrasts two ways to implement a business transaction.

 

ACID when you

BASE when you

Want every business transaction to succeed

Want to simplify design and development

Want consistent data

Want to get a business transaction started, even if it fails later

Don’t mind the complexity of compensating transactions

Can live with some inconsistency for a while (perhaps forever)

 

ACID transactions for consistency/integrity

Often, data integrity is important to business operations

A client wants a business transaction to succeed or fail completely before the server-side application replies.

A customer does not want to reserve a hotel room (using the sales agent microservice) that later turns out not be available (in the hotel administration microservice).

 

ACID means a transaction is atomic, consistent, isolated and durable.

Here, it means microservices are coordinated synchronously on the server-side.
So, clients’ service requests are not affected by how the server-side modularised.

 

Aside: For scalability, microservices can be replicated on parallel server-side nodes.

Sticky sessions or state replication enable a load balancer to choose which node responds.

 

To keep things simple, it is best if all the required data can be stored in one place.

Then, the database management system can be used to roll back a transaction when a business rule is violated.

It can also save some programming effort by maintaining referential integrity.

And support analysis of data for management information purposes.

If data is distributed, it may be necessary to hand-code the two-phase commit units a transaction manager could handle.

 

BASE for very high throughput by partitioning

Naturally, it is easier and quicker to develop a module that has minimal interaction with others.

For agility, developers want their microservice to work on its own, and reply to a client immediately with minimal dependency microservices.

For scalability, they want to isolate modules physically as well, deploying them on differ server-side nodes.

 

BASE means an application is basically available, scalable and eventually consistent.

Here, it means that one microservice may reply to a client before that client’s desired business transaction is completed.

Other microservices are coordinated later, asynchronously, and compensating transactions are performed if need be.

The result is that clients become aware of, and are affected by, modularisation into microservices.

 

Basically available

The idea is that a microservice can serve its clients well enough without immediate reference to other microservices.

A microservice can complete some business transactions immediately, and set other business transactions in motion.

The presumption is that a client will be happy to see a business transaction started, with no promise it will be completed.

E.g. a customer reserves a hotel room using the sales microservice, without the promise that the room will be available.

 

Scalable

Today, an ordinary relational database can handle many thousands of transactions per second.

Very high transaction volumes can be handled by beefing up the data server and using solid state drives.

To handle extraordinary transaction volumes, the data store can be partitioned in one of two ways.

·         Sharding: this means partitioning the data store into separate data populations (perhaps by geography or region?).

·         Functional scaling: this means partitioning the data structure by subject matter (e.g. customers and products).

 

Microservices can enable functional scaling, but this is not a general motivation.

FAANG is an acronym for five large IT-centric businesses, namely Facebook, Apple, Amazon, Netflix and Alphabet’s Google.

To which you might add eBay and Spotify.

Hard cases make bad law is legal maxim.

In other words, a general law is better drafted for average or common circumstances.

Not many businesses are like the seven mentioned above.

Whatever those internet giants do to handle extreme business transactions volumes is not necessarily optimal for your business.

 

Eventual consistency

Doing business using inconsistent data is a business issue before it is a technical issue.

Any business transaction that spans two or more partitions needs special attention.

The BASE strategy is to divide a business transaction into smaller transactions, each performed by a different microservice.

And to allow cases were a whole business transaction cannot be completed in one go.

You must analyse possibility that data becomes inconsistent, the impact that has on business operations, and when and how to restore data integrity.

E.g. what to do if a customer reserves hotel room (using the sales agent microservice) that turns out not be available (in the hotel administration microservice)?

You have to analyse and design additional compensating transactions to correct or undo the unwanted effects of business transactions that start, but cannot be completed.

Lessons from CBD history

Microservices can be seen as a renewal of the Component-Based Design fashion in the 1990s.

CBD partitioned a cohesive data model into chunks, each an aggregate entity maintained by a “business component”.

 

How did developers maintain data integrity?

Some worked to the presumption that few transactions stray beyond the bounds of an aggregate entity.

Some (as in domain-driven design) gave the root entity in each aggregate responsibility for transaction management.

I suspect these developers may have ignored some data integrity tests that could be made.

Still, what to do about those business transactions that do require access to data in more than one aggregate / business component?

 

Maintaining all the aggregate entities in one single data store

This facilitates queries, data analysis and reports.

The optional update patterns are

·         Synchronous ACID transactions maintain data integrity: Complete a two phase commit unit, integrate components via RPC or middleware; OR use the DBMS transaction management to maintain cross-component data integrity.

·         Asynchronous BASE pattern, allow data integrity temporarily: Reply before completing the transaction, then integrate components via using RPC or middleware to complete it, performing compensating transactions as need be.

 

Maintaining the aggregate entities in distinct data stores

This hinders queries, data analysis and reports.

The optional update patterns are:

·         Synchronous ACID transaction patterns maintain data integrity: Complete a two phase commit unit, integrate components via RPC or middleware; OR use a distributed/federated transaction manager.

·         Asynchronous BASE pattern, allow data integrity temporarily: Reply before completing the transaction, then integrate components via RPC or middleware to complete it, performing compensating transactions as need be.

 

Some options couple components more obviously that others; but all options couple components in some way.

Particular conclusions and remarks

Publishing "decoupling" as a general principle can accidentally encourage excessive decoupling of software components across a network and/or via middleware.

EAs ought be concerned about creating needless disintegrities and complexities, not to mention needless costs and performance issues.

The issues, principles and tradeoffs are largely vendor and technology neutral.

If EAs can't directly govern Technical and Software Architects, then they need to be socialised with Solution Architects who can do this on their behalf.

 

Assisting agile development is a plus for any modular design strategy.

A clean division into business components/microservices can suit the division of work between small teams.

However, remember Berrisford’s universal trade offs.

 

Agile developers’ dream

Enterprise architects’ nightmare

Smaller, simpler modules

Larger, more complex module dependency & integration

Module isolation/autonomy

Duplication between modules and/or difficulties integrating them

Data store isolation

Data disintegrity and difficulties analysing/reporting data

 

Be cautious about partitioning what could be one cohesive data structure.

Beware naïve principles, look at realistic requirements and assess trade offs.

·         Flexibility? Beware this usually requires a more complex design.

·         Scalability? Be realistic about the need, and assess the cost of allowing and restoring integrity.

·         Decoupling? Beware that decoupling related components physically does not decouple them logically.

·         Reuse? Beware that dividing an application does not usually create components readily reusable in other contexts.

·         Keep it simple? Beware BASE can be considerably more complex than ACID.

·         Agile development? Grouping transaction scripts into “pseudo microservices” is a rational way to divide work between individuals or teams.

General conclusions and remarks

In short, there is no silver bullet, you can’t have everything, there are always trade offs.

Read SOA as per the Bezos mandate for some SOA history.

Read Domain-Driven Design, Transaction Script and Smart UI for discussion of the simple v complex trade off.

Read Smart endpoints and dumb pipes for discussion of the synchronous v asynchronous trade off.

 

Programmers are intelligent people, and know useful stuff.

Having a programming background helps people become good architects.

It gives them insights that are valuable at the architecture level.

 

Nevertheless, there are difficulties out there.

·         Technology vendors encourage programmers to use technologies they don’t need.

·         Industry analysts repeat what technology vendors tell them.

·         Reading software guru books can encourage programmers to use patterns designed for problems they don’t have.

·         Some programmers are under-educated in the very wide variety of ways to code software – and trade offs between them.

·         Some programmers like extend their CV with the latest buzz words.

 

Advances in hardware can always be negated by questionable software engineering practices.

The ability of software to consume ever more electricity is endless given presumptions such as:

·         Following latest design fashion without question.

·         Copying what Google or Amazon do.

·         All components should be loosely coupled.

·         All inter-component messages must be sent via middleware.

·         All components should be open to extension but closed to amendment.

·         Integrity is best achieved by eventual consistency.

·         New data store types are better than relational databases.

·         Complex data abstraction layers are needed.

·         Microservices are the answer.

 

The result is that programmers can easily generate solutions that are more complex than is justifiable.

Complexity can appear in excess code, data abstraction layers, message passing, compensating transactions and middleware dependency.

 

Vendors, industry analysts and commentators encourage programmers to decouple application components.

Concerns about this include:

·         there are a dozen ways to be coupled or decoupled, so the meaning of the direction is unclear.

·         physical decoupling is not logical decoupling.

·         decoupling tends to increase complexity, slow down and disintegrate business processes.

·         the optimal degree of coupling varies with the granularity and cohesiveness of the specific components at hand.

·         following the direction has led to overuse of middleware and consumes excessive server-side resources.

 

As Craig Larman wrote: the problem is not high coupling per se, it is high coupling between components that are unstable in some way.

Rather than directing decoupling throughout, architects should be balancing trade offs and providing more nuanced guidance.

 

How to save the enterprise from the cost, risks and issues of following software design fashions?

EA needs solution architects to shape and steer programming efforts in an EA-friendly direction.

They have a role to play in abstracting and repeating lessons learned from experience.

 

Don’t be over eager to use the latest design pattern/method or technology.

Beware that ungoverned agile development can lead to duplication, disintegrity and complexity.

Keeping things simple, minimising unnecessary code and use of middleware, is a reasonable principle.

This can sometimes mean constraining programmers’ enthusiasm for “new” ways of doing things.

 

As the first slide in our enterprise and solution architect training says.

Think about the business context.

Don't forget the numbers.

There are many ways to design and build something.

You have to balance trade offs:

·         between qualities (e.g. flexibility or simplicity)

·         between what is best for the local system and what is best for a global system.

You are responsible for:

·         knowing there are many possible answers

·         ensuring trade offs are addressed and

·         recommending one or more options.

 

Footnotes

Design practices some relate to true Microservices

In 2002, Martin Fowler observed that Domain-Driven Design is difficult to learn and best reserved for complex systems with “rich domain models”.

And in January 2017, Wikipedia said: “Microsoft recommends that [domain-driven design] be applied only to complex domains.”

(Where I guess rich implies substantial use of inheritance.)

 

Read Dividing an application into microservices and Domain Driven Design, Transaction Script and Smart UI for practices related to microservices.

Domain-Driven Design, CQRS and Event Sourcing are sometimes related to each other.

But Transaction Scripts can equally well publish Events (for others to consume) and log Events (for subsequent query and replay).

And you don’t need Domain-Driven Design or CQRS to design microservices, separate database update transactions from queries, publish Events, or use Event Sourcing.

 

Design by Contract (DBC) means a server will fall over if a client (any client) does not guarantee its preconditions.

Defensive Design means a server tests (or likely retests) its preconditions are met, and responds gracefully if not.

Distribution of microservices (or any software) between nodes tends to increases the need for Defensive Design.

 

You might see microservices as a scaling down of the “Bezos mandate” and/or Service-Oriented Architecture.

In other words, as extending the principle that components are distributed across a network and communicate via APIs.

However, microservices do not have to be distributed across a network.

Other modularisation strategies are no less service-oriented; and SOA does not say how to divide an application into modules.

 

(For more on this read SOA and the Bezos mandate).

Linkedin comments and discussion

 

Marc Bastien wrote

At last, a post on architecture with some real content.

Thanks Graham for elevating a bit the level of discussion about IT architecture above vendor sales pitches.

And for those that have a hard time understanding your post, that are lost in the terms, and find this subject too "esoteric", here's a friendly suggestion:

Don't rely too much on programmers that are good at doing the "real" thing.

Because most of them just don't have a clue about the impacts of what they are doing besides and above the code they deploy.

Not because they're​ not bright enough, but simply because they're busy doing their things.

 

Martin Cotter wrote:

This is worth reading again and again and sticking in the diary to read again.
The comment by Marc about coders who are brilliant but busy rings especially true.

 

Rob Dean wrote:

Watching a presentation on Microservice Architecture I was astounded by the lack of perception [of the points in this paper].

You cannot provide every quality attribute in any architecture.

Trade offs are the nature of engineering, regardless of the discipline.

Each service will contain some redundant code to enable it's isolation from the other services to allow for the horizontal scaling.

While removing one type of dependency you introduce others, such as state.

The only way to introduce reuse in these services is composing them from a configuration definition.

Also, while attempting to scale small units of functionality you introduce dependency scaling.

If you decouple Orders from Inventory as in the example, you will always need enough inventory services to support the orders.

The dependency remains intact even though the monolith gone.

 

Edin Nuhic wrote:

Graham, your writing on the subject is highly relevant.

TOGAF can often confuse people who misunderstand it.

However, you seem to be the one who analyzes things deeply and then tries to summarize what it all comes down to.

Your analysis of loose coupling is a poetry that everybody getting into system integration should read several times until it is really understood.

As you point out - there are two definitions of microservices

·         the one from Martin Fowler that addresses the organisational and the deployment aspects in the first hand

·         the other that addresses the performance.

 

This second advocates asynchronicity as a means of preventing idle slots waiting for a synchronous service to return.

[This bring something new to the table] and deserves a separate discussion.

It is strange that very serious people can mean so different things and call them the same name.

A few readers’ questions

 

-1- What makes a service a micro?

It is micro compared with the macro application it is a component of.

 

-2- What is micro about “pseudo-microservices”?

They are similar in size to true microservices.

Both process a subset of service/transaction requests from the client layer.

But pseudo microservices don’t wholly encapsulate the data they access.

 

-3- How does a microservice differ from a usual SOA style service?

What is a usual SOA style service?

 

-4- How important is independence of deployment - the devops aspect?

That suggests a distinct application, not a subdivision of one.

Unless it means deliberately dismembering a business process.

 

-5- How important are performance gains through asynchronicity and granularity.

Speed – could make things worse.

Throughput – important in some (probably rare) cases

Availability – it depends what the business wants to be available.

 

All free-to-read materials at http://avancier.website are paid for out of income from Avancier’s training courses and methods licences.

If you find the web site helpful, please spread the word and link to avancier.website in whichever social media you use.