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.
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:
- 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.
- 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.
- 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.