Sunday 26 July 2015

Microservices Design




The term "Micro-service" is getting attention these days, some big names are using this style of design. I was introduced to it by looking at Netflix technical publications, which is similar to the industry that I work in, at the moment.


Currently there is no organisation that defines what exactly this design style is, however there seems to be some consensus around what "Micro-services Architecture" is.


Monolithic vs. Microservice

Comparison is usually a nice way for learning a new concept. So let's compare Micro-services Architecture to Monolithic.


A Monolithic application is single unit application. Think about a web application, HTML/Javascript client side, a web server, which receives client requests, and consolidated it's database to generate html and return to client. We want to focus on server side.


The server side application is a monolithic executable, a bulk. Updating the application means updating the executable and or database. If the application goes down, non of the features of this application are accessible. usually it is all written in the same programming language, and runs in the same process(, communication between modules is in the same process). And finally to scale horizontally you just need to run more instances of that application ( i.e.: load-balancers and more web servers).


When


Monolithic architecture can be successful, however in some situations the characteristics of this style of design may not be what you want, They are NOT so compatible with :
  • Horizontal scaling:  It may not mean having to scale all the features of your application. Only some areas of your application may need to be scaled and each by a different factor and at different times.
  • Different areas of your application may be written easier and with better performance using a different language or development paradigm(Nodejs, go, python, C++,..), and platform.
  • Development cycles need to be independent: A team has to wait for all others to finish(design, develop, test,... ) before they can deploy their work.
  • Organised around business capability rather than organisation's communication path ways..  For example instead of having a UI, Middleware, and database team for the whole application. You will have a team of each category around each service or business capability.
Characteristics

In Micro-services the flavour is around product mentality, rather than project. In this style there is an ongoing work in the software and the team is more focused on linking business capabilities  and providing more features for the customer. This is even extended to the fact that development team is responsible for deploying and maintaining the running instances.

It seems that micro-services are about smart endpoints and simple pipes. Consider restful http services, versus ESB(enterprise Service Bus)that can transform, apply business rules and orchestrate activities, using WS- distributed transaction protocols . In micro-services each service is conceptually decoupled and features are as cohesive as possible. In a more complicated case some sort of message queuing may be use, i.e.: RabbitQ.

Micro-services are Asynchronous, this increases performance, and respects the distributed mode considering congestions, failures,.. .

Data modelling and persistence is decentralised, meaning that each service models the world in their own relevant terms, and persist them in any format using the technology that makes the most sense for them, RDBMS, NoSql, SQL server, MongoDB, Cassandra,.. .

Automation and monitoring, are more advanced that monolithic applications.The pipeline that starts with building and unit testing and ends in deployment to production, needs to be automated. In Netflix they have the concept of bakery, where they have template instances, then they bake an service update into a machine, and it is released all with or without click of a button.
In a world that many services are running you need to know: about the statistics of your services, how they perform, track messages, and monitor the health of the services.

A consequence of using services as components, is that applications need to be designed so that they can tolerate the failure of services. Any service call could fail due to unavailability of the supplier, the client has to respond to this as gracefully as possible.
This is where some patterns have emerged : tolerant reader, throttling,.. .

There are some limited number of domain / contexts that each developer can have in mind work with, using micro-services is compatible with how many contexts  a developer can handle, and different skills they need to have. So with smaller teams and more focused there are people with more skills, and less communication overhead.
  
Service Granularity

There are disagreements about how many lines of code a service needs to be, however this seems to be irrelevant. What matters is the level of granularity we are prepared to introduce. This may relate to lines of code, but that's irrelevant to the idea of micro-services.
Please refer to the diagram on the top observing how some factors change as we move from and monolithic application towards having more and more services.

Modelling the right way is what need to be cared about. Domain Driven Design approach (bounded context) could be a useful approach in this style of design.


Challenges

In a way, with micro-services architecture some concepts will be pushed into runtime rather than development or build time. This raises issues if you are not prepared for them, specially after you have released to production.


However these can be mitigated by using some techniques and tools.


Versioning: Each micro-service can have it's own development and release cycle. Consider service A depends on Service B(version 1). What happens when Service B (version 2) is released? 

Contracts: To make it even harder, service development might be like silos, one team not knowing what the other ones are providing, how can Service A know what to expect service B? We are not talking about interface, but the "needs and provides services".


Testing: When testing, service A is going to mock service B, how do developers know what is the behaviour for service B that they need to mock?


Service Boundaries: Another issue is how to define service boundaries? a part of the answer is the service granularity, and the bigger part of the answer could be domain driven design(bounded context).


Transactions: The concept of a transaction which used to run in a process, will changes as we go distributed. It adds overhead to your services.


Monitoring, and tracking : you will need tools for monitoring the health of the services, to see how they are doing, to see which ones are failing.

A mature micro-service style application needs to have tools to spin new servers automatically or manually in a matter of few minutes if not seconds.

Summary


Micro-services are the trend these days, they stand against monolithic applications, they are another way of designing applications, with some pros and cons.


The amount of preparation you need to do to get ready for micro-services is considerable, continuous delivery,automation, testing strategy, deployment ... requires you to have a top shape product and supporting infrastructure.


Start simple( with a monolithic application), and break chunks off it bit by bit and move them to micro-services, however apply . You need to confirm that the business model works first, then try to move.


References: Martin Fowler, Netflix articles, Ryan Murray & John Napier.  

Thursday 2 July 2015

Attribute Driven Design

So far I have talked about what tactics are, and how they fit into architectural design patterns and styles.
I encourage you to read Deriving ArchitecturalTactics: A Step Toward Methodical Architectural Design .

You have all the bits of knowledge, however you are going to need a methodical way of putting all this together and design the system. 
This is where Attribute Driven Design comes to the scene.

ATTRIBUTE DRIVEN DESIGN.

I tried using it, and in the beginning it was hard to follow, but you will get the hang of it eventually. This method is used to create an architecture down to a few levels of detail that satisfies the Quality Attributes of a system. It creates the main structures for the QAs.

Inputs include these architecturally significant requirements:

  • quality attribute requirements 
  • design constraints
  • functional requirements

Outputs include
  • first several levels of module decomposition
  • various other views of the system as appropriate
  • set of elements with assigned functionalities and the interactions among the elements 
 Steps to follow:
Functional requirements define what a system should do to meet stakeholder needs. For example :
 - Users should be able to view their account activity.
 - Users should be able to buy and sell goods.

 Design constraints are decisions about a system's design that must be incorporated the final design of a system. Examples:

 - Should use CouchDb as storage.
 - Should use http as a communication protocol.
 - System shall run on both Unix and Windows.

Quality attribute requirements are the  requirements that indicate the degree to which a system must exhibit various properties. For example:
 - The system must be build-able within six months.
 - The system shall process sensor input with in 1 sec.
 - The system shall allow unit tests to be performed within 3 hours with 85% path coverage.  

And don't forget all these can be implied in one another. For example:
 - "Given that Joe is the only resource available to manage persistence storage, and he only knows oracle", means system should use oracle.
-  "Given that market demand will increase dramatically in the next six months", means that the system must be build-able within 6 months.


Now Let's have a look at steps:

STEP 1:

In essence, you make sure that the system’s stakeholders have prioritised the requirements according to business and mission goals. You should also confirm that there is sufficient information about the quality attribute requirements to proceed.

STEP 2:

In this second step, you choose which element of the system will be the design focus in subsequent steps. You can arrive at this step in one of two ways: 

1. You reach Step 2 for the first time as part of a “greenfield” development. The only element you can decompose is the system itself. By default, all requirements are assigned to that system. 

2. You are refining a partially designed system and have visited Step 2 before.4 In this case, the system has been partitioned into two or more elements, and requirements have been assigned to those elements. You must choose one of these elements as the focus of subsequent steps. 
In the second case, you might choose the element based on risk and difficulty,business criteria, organisational criteria,.. .

STEP 3:

At this point, we have chosen and element of the system to decompose, and stakeholder's prioritised list of requirements that affect the element.
stakeholders have put High, Medium, Low next to each requirement, indicating how important it is to them. Then the architect will also put High,Medium,Low next to each requirement, indicating the potential impact of the requirement on the architecture.
Then you will have pairs of values for each requirements:

(H,H),(H,M),(M,H),(M,M),(L,H),(L,M),...

Just notice that further down the design, and after some analysis you may find out your assumptions need to be changed, so change it, and choose the drivers again.
five or Six candidates would be enough to go forward.

STEP 4:

At this point, we have chosen and element of the system to decompose, identified the candidate architectural drivers. Now we need to choose our design concept, which means choosing the major types of elements and the types of relationships among them.
Design concepts and QA requirements help you achieve this.

You can follow a methodical set of steps to derive this:

Identify the design concerns that are associated with the candidate architectural drivers. For example, for a quality attribute requirement regarding availability, the major design concerns might be fault prevention, fault detection, and fault recovery.

For each design concern, create a list of alternative patterns that address the concern.
identify each pattern’s discriminating parameters to help you choose among the patterns and tactics in the list. For example, in any restart pattern (e.g., warm restart, cold restart), the amount of time it takes for a restart is a discriminating parameter.
Select patterns from the list that you feel are most appropriate for satisfying the candidate architectural drivers. Record the rationale for your selections.
You can create a matrix of patterns pros, and cons, versus each architectural driver.
Choose which set of pattern, combinations or new patterns you want to use, and record your rational.

Review, evaluate, and refine.  

At this point:
 - You have decided on an overall design concept with major component type and relationship among them.
 - You have assigned some functionality to each element.
 - You have decided on type of relationship: remote call, local call,sync, async., .. .
 - The requirements of of the elements, data models.
 - 

STEP 5:

At this point, you instantiate the various types of software elements you chose in the previous step. Instantiated elements are assigned responsibilities according to their types; for example, in a Ping-Echo pattern, a ping-type element has ping responsibilities and an echo-type element has echo responsibilities. Responsibilities for instantiated elements are also derived from the functional requirements associated with candidate architectural drivers and the functional requirements associated with the parent element. At the end of Step 5, every functional requirement associated with the parent element must be represented by a sequence of responsibilities within the child elements.

STEP 6:

you define the services and properties required and provided by the software elements in our design. In ADD, these services and properties are referred to as the element’s interface. Note that an interface is not simply a list of operation signatures. Interfaces describe the PROVIDES and REQUIRES assumptions that software elements make about one another. An interface might include any of the following: 
 • syntax of operations (e.g., signature) 
 • semantics of operations (e.g., description, pre- and postconditions, restrictions) 
 • information exchanged (e.g., events signaled, global data) 
 • quality attribute requirements of individual elements or operations 
 • error handling.

Step 7:

You verify that the element decomposition thus far meets functional requirements, quality attribute requirements, and design constraints. You also prepare child elements for further decomposition. 

NEXT:

Once you have completed Steps 1–7, you have a decomposition of the parent element into child elements. Each child element is a collection of responsibilities, each having an interface description, functional requirements, quality attribute requirements, and design constraints. You can now return to the decomposition process in Step 2 where you select the next element to decompose. 

I have used SEI's material to write this post, if you are interested in more details on this method, their web-site has a couple of good articles on this.