EWF Service Decomposition Pattern

EWF is another microservice decomposition pattern that I like to use when building services. It stands for Entity, Workflow & Functional services. In simplest terms, it’s an extension of Single Responsibility principle and allows for building independent testable services. To understand this pattern better, let’s start with an example. Imagine I want to write a signup service that allows user to signup to a website using their Google account. The service would perform the following: 
  • Let user login into google account and fetch user’s basic information from Google upon successful sign-in.
  • Apply more default settings associated to creating new user. 
  • Persist the user in the database. 
  • After successful registration, redirect the user to the homepage. 


At a high level, this works just fine and gets the job done. But if you think about testability first and wants to test this service in isolation we have a problem! The entire testing paradigm relies on the fact that the OUTPUT should have a direct correlation with the input. In this case the input is a signup request but output produced is a redirect response. There is simply no way to test this service in isolation. i.e I cannot test if the defaults are applied correctly or if the user is persisted correctly in the DB using the service api tests. One could use unit tests to test these individual functionality but in microservice architecture, a service is an unit in itself exposed as apis, we should be able to test them in isolation. More thoughts on this topic here and here. Read more about how to test service in isolation here.

Let’s try to apply EWF decomposition pattern by identifying the different parts:

Entity: User is clearly the entity here. We will need to Get/Save/Delete user from the DB. Entity service should only worry about saving and retrieving User from the storage and should avoid any funny business logic. This would allow me to keep the separation b/w business logic and entity and making sure this service is only concerned with persistence and exposing it over API to keep the loose coupling wrt rest of the stack.

Functional: We need to identify the functional parts in this workflow that can be performed w/o mutating the state of the system. In this case, fetching user information from google and apply more default settings to create a domain but non-persisted user object. This is arguably the most important business need of this workflow. Encapsulating this piece in a service would allow me to test if defaults are applied correctly in isolation and w/o having to worry about persistence.

Workflow: This would bootstrap the entire workflow: 1)Create the non-persisted domain user object by calling the Functional service API 2)Persist the user by calling the User Entity service 3)Redirect to homepage. This would allow me to test the workflow w/o having to worry about actually creating the entities or rely on integrations tests to test the workflow.




The benefits of decomposing Entity, Workflow and Functional services other than the obvious benefits of decomposition are:
  1. Entity services provides better encapsulation over the domain model by creating loose coupling and only access through APIs. Extracting out creation of complex domain entity from its persistent allows for better testability. 
  2. Functional service carry the heavy business logic and are highly testable. These services ideally should not be mutating any state in the system. This makes  them perfect candidate for DarkLaunches in the production w/o having to worry about the impact on the overall system.
  3. Workflow services allows ability to test workflows at the component level tests rather than relying too much on the integration tests.
Personally, i feel very confident that the overall workflow will work in production when the individual participating services have been designed for testability and tested thoroughly in isolation. 

Popular posts from this blog

Break functional and orchestration responsibilities for better testability

Microservices and Tech Stack - Lessons Learned

Prefer Consumer over Integration Tests