Service Boundaries — the Biggest Puzzle of a Microservices-based Platform
Determining the service boundaries is not a trivial task — it’s the single most important puzzle in the Monolith->Microservices Journey. When left open, in absence of a framework and guidelines, the results of domain decomposition could vary and become inconsistent. The usual tendency or approach is to look at the monolith codebase and start the decomposition exercise from there. Such thinking (aka “inside-out”) can overlook the customer experience, use cases, and their needs and often produces a design that leaks the implementation details into the exposed APIs. Worse yet, it may exhibit a company’s internal organizational structure (based on who will own what), produce a design with hidden dependencies, and create tightly coupled consumers. An architecture built with “Inside-out thinking” is hard to evolve, scale, extend, and consume.
The other approach is to look at the monolith’s codebase, learn the customer use cases, document them, identify various customer personas, and then do the domain decomposition exercise in a customer-centric way by validating the capability from an outside-in standpoint through the identified customer personas on how each of them consume the capabilities. The evolution of these capabilities is iterative, so features are improved or added only when the customers truly need them (added/improved through learning/feedback from the customer’s usage). The data model also reflects what the customer needs from the capability rather than what is available in the monolith codebase. At Greenlight, we chose to go with this approach.
But, how do you practice customer-centricity, make it repeatable, and scale this thinking throughout the organization?
At Greenlight, we came up with a framework which we call “Greenlight-as-a-Service” (GLaaS) to drive our platform execution strategy at scale. The GLaaS framework comprises the following key building blocks: Customers, Principles, Product Architecture, and Standards & Guidelines.
Customers
When you think of customers, the usual thinking points to the users of your mobile or web app. At Greenlight, we have expanded the definition of our customers to include anyone who consumes the capabilities in the Greenlight Platform. With this thinking, we’ve three types of customers: Consumers of our consumer App, Greenlight developers, and Partner developers.
Consumers
Consumers are the ones that consume functionalities from the Greenlight Platform through the Greenlight branded App. Today, they are represented by the parent or the child users of our mobile app.
Developers
Developers are the Greenlight software engineers that develop applications and solutions for Greenlight. Just like we need to provide a great consumer experience, we also need to provide a great experience for our developers building these solutions. Why? Remember, we need to maximize the effectiveness and productivity of our developers because they are a very important organizational asset. Plus, productive developers are happier developers, who tend to be more engaged and contribute more overall value to the organization. This is just human nature and it’s why it makes sense to invest in a software development experience that reinforces what it takes to build great software, quickly. A well-designed platform is one component to that puzzle.
Partners
Partners are also developers, but they are members of partner organizations, not Greenlight. We often think of these as “external” developers. They are important for similar reasons as internal developers. Their success is largely dictated by their ability to rapidly understand and integrate with Greenlight platform capabilities. Just like Greenlight builds applications on top of the platform, partners do too. They may build completely stand-alone, full-featured apps, or they may integrate with certain parts of the platform to expose data or integrate certain business processes. Regardless, the quality of experience we provide to them is directly related to the quality of results and the success of the partnership. If we provide them a terrible experience, they are less likely to succeed and less likely to continue to invest in the relationship. Business people may sign the deals, but partner developers deliver the value. It’s essential we think of and treat them like first-class customers to maximize the value of our partner engagements.
Platform-as-a Product
Our goal is to maximize the satisfaction of all customers. What do products do? They solve the needs of customers. When it comes to developer and partner customers, this effectively means we need to think about and measure platform success from a product perspective.
It’s important to pause to note how different this is. Traditional approaches often treated the functionality encapsulated in the platform as a technical problem or byproduct of the technical architecture. The platform was an engineering problem. This new way of thinking forces us to think about the platform from an “outside-in” or customer point of view, which has profound implications that we will explore further in this blog.
The emerging idea of treating APIs like “products” is very much driven by the realization that adopting product thinking and customer-driven approaches to defining and validating how we expose our platform capabilities can lead to greater developer and business success. If you don’t understand and think about how to make a developer successful, you likely won’t end up with a system or set of primitives they can use that are optimized for their needs. The platform must be customer-centric.
At Greenlight, we’ve adopted this new way of thinking to build the Greenlight platform- the Platform-as-a-Product mindset.
The above high-level diagram helps illustrate how the GLaaS platform services the needs of developers and partners who, in turn, service the needs of end consumers. Each has a very well-defined but different experience, tailored to their specific needs and optimized for their success. Overall success is highly dependent upon how well we service the needs of each.
Principles
The second building block is a set of “Principles’’ that encourage a consistent and high-quality developer experience. Principles are core tenants we use to shape our decision on how we design and evolve the GLaaS platform. There are 7 core Principles that we follow when evolving the GLaaS platform.
Modeled around Business domain
A service must be modeled around a business capability defined in the domain model of that service’s domain. The service’s APIs must be designed with consumer use cases and consumer needs in mind, not simply how an engineer imagines they should reflect internal implementation details. Again, this is outside-in, not inside-out thinking.
What this means:
- A service cleanly maps to a business capability in the service domain’s business capability model.
- A service’s interface (aka API) and its data model should only expose operations to perform the business operations of that capability.
Encapsulation
All services MUST encapsulate the internal business logic and implementation concerns, and abstract them from the clients- such an approach creates loose coupling between the service and its consumers. When a service exposes internal implementation details in the service’s API operations (even accidentally), consumers tend to rely on them without the service knowing anything about how such things are used. Since both business logic and internal service implementation are expected to change over time, leaky abstraction makes it extremely difficult to evolve the consumer or the service independently without requiring changes in both.
What this means:
- A service must not leak internal implementation details in the form of field formats, internal identifiers, or data models (for example, which database it uses, the database IDs, which service provider it interacts with, etc.).
- Consumers must access the service’s functionality only via its APIs and domain-external interfaces. They must not access the service’s database.
Autonomous
A service MUST be designed to be autonomous. A service should exhibit high functional cohesion with a single and well-defined purpose and should be an independent deployment unit.
What this means:
- The service’s deployment package must include all its dependencies.
- The deploy/undeploy of a service must not require deploy/undeploy of another service.
- Services must not be required to be deployed in any particular order.
- A service must be able to evolve independently of any other service.
Consumer first
A service exists to enable a client to realize its use case, so every service should prioritize consumer experience over everything else. A service that is difficult to consume reduces the benefits of the platform and encourages clients to look for alternate options.
What this means:
- All service contracts must be discoverable and well-documented.
- The documentation must describe all non-functional concerns, such as failure modes, security requirements, duplicate request handling (as appropriate), versioning (deprecation, if any), and SLOs (Service Level Objectives).
- The service contract documentation must be made available via industry standard machine-readable artifacts (e.g., OpenAPI for REST APIs) so consumers can generate client artifacts from the contract definition.
- API contracts should use consistent vocabulary that relates solely to the business language, as defined in the domain model of that domain and the consumer use cases.
- APIs must exhibit a consistent behavior for both functional and non-functional features.
Stable
APIs are forever. While this sounds like an overstatement, it’s largely true. APIs are extremely hard to retire once adopted, which is especially true once clients outside your direct control start using them. As such, we must ensure that APIs are never changed in a way that would break the existing client integrations.
What this means:
- All API contracts must be versioned and any changes to the APIs must only be done through versioning.
- APIs must not break compatibility within a particular version.
- The API documentation must describe features/changes in each release version.
- A new version of an API must not require existing clients to reintegrate.
Reusable
Services must not assume specific use case contexts or specific client contexts. A service is a capability of a domain so it must be assumed that it will serve in multiple client contexts, use cases, and access channels, either via direct invocation or via other higher-level services. One of the primary goals of the API platform is to be able to quickly develop new services or product offerings by composing one or more services, so thinking about building services that can scale beyond immediate contexts helps towards these goals.
What this means:
- It should be easier to add incremental functionality over the base functionality of the service.
- Higher-level services should be able to easily compose lower-level services as needed to provide higher level business functionality required by end user applications.
Externalizable
All services must be built to be externalizable. Services must not assume that they are being consumed either externally, or internally. A service, once it is made available, should instead assume that it’s consumed within a domain, or by other domains, by partners, or any other external client applications. This is the true business value of a platform. The same investment can be reused multiple times by every client who needs that capability.
What this means:
- Externalizing a service should only require adding specific authentication and authorization methods as required to protect the service operations externally.
- The overall functionality, the API contract, or the service implementation (including the service’s business logic) must not require changes, for it to be externalized..
- The exposed API should be available over the standard protocol that the consumers can integrate with. For example, at Greenlight, all services must be available over REST as this is the protocol universally accepted by business customers (More on the API protocols used at Greenlight and their rationale in a later blog post).
Product Architecture
Product architecture defines the shape of the Greenlight platform. You can think of it as the high level blueprint that defines the boxes and boundaries of what the platform provides.
As stated previously, one of the biggest challenges in a Microservice-based platform is determining the service boundaries. You often hear something to the effect of, “a Microservice shouldn’t be too big or too small”, or “a Microservice team should be small enough so it can be fed by 2 pizzas (famously known as two pizza team rule). While these are all correct, they’re not very helpful in practice. Also, you can pick your best senior and big picture engineers to do this job for you, but it’s not always a scalable and durable model. First, a single person winning a lottery shouldn’t be a threat to the viability of the platform, Second, these individuals can’t be allowed to become bottlenecks for teams in the platform journey advancement, Third, since no two individuals are the same and everyone has varying levels of experience, the outcomes can vary widely with the pieces not looking like a part of a coherent whole. As a result, you need a framework with a common set of objective (rather than subjective) principles and guidelines that, when followed, will produce consistent results.
Domain-driven design (DDD) is a well-established industry framework for determining service boundaries. DDD has two distinct phases, strategic and tactical. The strategic phase defines the large-scale structure of the system and ensures that the product architecture always remains focused on business capabilities. The tactical phase defines patterns, such as entities, aggregates, and domain services that you could use to create the domain model — and then define the service boundaries. At Greenlight, we adopted DDD as the framework for determining the service boundaries. The “Greenlight Platform Meta Model” (depicted below) is Greenlight’s DDD framework, driving the product architecture at Greenlight. It defines the complete set of patterns and principles for performing domain decomposition, model business capabilities and service boundaries, and defines the API products and its resources, all at one time.
In the meta model:
- The domains are Greenlight’s core lines of businesses, or they are supporting domains that contribute to delivering the core domain. For example, Greenlight wallet, invest, spend, earn, save, give, and learn are core domains. Risk, identity, payments, transfers, and compliance are supporting domains.
- The meta model defines the “domain” as the bounded context where you would define the domain model (a.k.a., ubiquitous language) for that domain.
- The meta model enforces domain decomposition by business capabilities. We arrive at these business capabilities by applying the tactical DDD patterns “Entities”, “Aggregates” and “Domain Services” to the bounded context.
- These business capabilities form the service boundary of a Microservice in the GLaaS platform.
- The service boundary is the level at which you define your API product (or a platform product). The service boundary exposes the API operations of this API product to its consumers.
- In the same manner that many business capabilities compose to form a business domain, many API products (capabilities expose their functionality through APIs) compose to form API namespace. In other words, the domain name maps to the API namespace 1:1. The API namespace is reflected in all API’s resource URIs and operations. Grouping by namespace helps with differentiating polysemes across domains or namespaces.
- The API resource (categorized as an entity resource or a controller resource) is either the aggregate’s root entity or the domain service name.
By following the meta model, we could arrive at business capabilities that are customer-focused, and independent of the organizational or technology structure. The capabilities are durable and any change in the organizational structure has no effect on them.
Below is an example of business capabilities from the identity domain by following the principles and patterns in the “Greenlight Platform Meta Model” (The Identity domain model below is only for the purpose of illustration-it’s not a comprehensive set of capabilities).
- Accounts- The capability that manages the lifecycle of a Greenlight family account and its users- — Aggregate.
- Applications- The capability that manages the lifecycle of a client application (a client application that makes API calls)- — Aggregate.
- Tenants- The capability that manages the lifecycle of a tenant, its policies for the purpose of Multitenancy- Aggregate.
- Authentication- The capability that handles the authentication and session management of Greenlight Users (A parent or child) through credentials, such as username/password, application credentials, or SSO- Domain Service.
- Links- The capability that manages the lifecycle of an Identity linking between Greenlight and a Partner- Aggregate.
- Consents- The capability that manages the lifecycle of user consents and agreements- Aggregate.
- Verifications- The capability that manages the lifecycle of an Identity verification (e.g. verification of email, phone, etc.)- Aggregate.
- Authorization- The capability that performs user authorization to APIs (coarse-grained: via oauth2 scopes; or fine-grained: via application business logic permissions)- Domain Service.
- Policies- The capability that manages the lifecycle of various identity policies (e.g. Identity, Tenant) — Aggregate.
- Developer Accounts- The capability that manages the lifecycle of an external developer account for the purpose of managing API access credentials (prod/sandbox), event subscription, Metadata Registration (such as public keys to validate a partner assertion etc.)- Aggregate.
- Credentials- The capability that manages the lifecycle of Greenlight user (Parent/Child) credential (user name, password, resets, security questionnaires etc.)- Aggregate.
- TOTP tokens- The capability that manages the lifecycle of a TOTP token- Aggregate.
- Onboarding: The capability that handles the onboarding/registration of a new user to Greenlight- Domain Service.
From the diagram above, there are 10 aggregates (Accounts, Applications, Verifications, Tenants, Links, Policies, Consents, Developer Accounts, Credentials, TOTP Tokens) and 3 domain services (Onboarding, Authentication, Authorization) that form the service boundaries.
Platform Layers
Another important part of the product architecture is to think about platform products in two different layers: the business platform layer and the foundational platform layer. Thinking about product architecture in this way helped us create a common set of reusable platform capabilities addressing horizontal concerns and can be used in any business context. For example, the platform products in the business platform layer directly consume the foundational platform layer capabilities in a variety of contexts or compose many capabilities together to deliver higher-level capability in the business platform layer.
Standards and Guidelines
The fourth platform building block is the standards and guidelines. The platform products in the GLaaS platform expose their functionality to the consumers via APIs. In line with our platform as a product thinking, where the goal is to delight the customers, these APIs should be intuitive, easy to consume, self-service, and exhibit themselves as part of a coherent whole rather than a disjointed set of products with inconsistent behavior among them. The alternative is forcing a developer to go through a learn-unlearn-learn cycle every time they integrate with a new platform capability, which is clearly undesirable. Establishing consistent standards, policies, and guidelines addresses these problems and helps drive consistency among the platform products.
The “Standards and Guidelines” define:
- Actionable API standards for services to design their APIs, document, and make them available to the end consumer. Some of the examples that the API standards define are: API resource styles, Naming Conventions, Protocol specific standards for Methods, Headers, and Status Codes, Resource Identifiers, Filtering, Common types/vocabulary, Field formats, Error Handling, API versioning, Service interaction design patterns, API Security standards, Bulk operations, and so on.
- The Service Implementation Guidelines for the Microservices in the Greenlight platform. Some examples of the service implementation guidelines are: Microservice Refactoring, Service Template, Technology stack-specific guidelines, Service communication concerns (discovery, security, resiliency etc), Cross service queries, Service composition, Data access patterns, Observability, CI/CD, Test guidelines, Event communication guidelines, and so on.
We’ll provide you more information on Greenlight’s Actionable API standards, and Service Implementation Guidelines in future blogs.
What we learned
- Defining service boundaries is one of the most difficult tasks when moving to a Microservices-based Platform. Using a framework like DDD helps us to think objectively, create service boundaries that we can reason about, and helps us to always remain focused around business capabilities.
- DDD has a steep learning curve so it takes some amount of time and effort to train individuals, but once there’s a shared understanding of the DDD patterns and guidelines, the benefits are real and yield consistent results.
- It’s important to define what DDD means to your organization so that it’s objective enough for anyone in the company to model the domain (not just a few busy individuals). For example, in the strategic DDD phase, whether to decompose the problem space by sub domains or by domains is usually a discussion that can spiral and continue forever. Although, neither is wrong, it’s important to pick one of the approaches you will use for the decomposition and proceed- whatever you choose becomes the bounded context from where you will define the domain model (a.k.a. ubiquitous language) by applying the tactical DDD patterns, such as entities, aggregates, and domain services. The DDD framework that you come up with should also describe how the domain model is implemented through the Microservices and APIs. If you don’t complete this important step all modeling remains on paper, a wasted effort, and you won’t have any tangible benefit or the north star you’re aiming for. The “Greenlight Platform Meta Model” solves exactly this problem.
- Product thinking is the core part of building a platform. Practicing product thinking allows you to come up with boundaries from the perspective of what the customer needs are and how a customer consumes the capability. This doesn’t mean you should ignore the existing codebase and use cases, but it means that whenever you come with a service boundary and its data model, you should validate the assumptions from the perspective of an end consumer use cases. This is a powerful paradigm, and always keeps you in check from getting overwhelmed by internal implementation details, and exposing them in the API contracts without being aware that they will introduce many consumption issues.
- Big bang domain modeling and platform exercises often fail. We recommend that you start small and focus on important areas first. For example, you can start with the domain modeling exercise for part of the monolith codebase functionality and implement them in the form of new Microservices that a new partner wants to consume externally. The iterative platform development model is working very well at Greenlight. Also, DDD as a framework complements really well with the iterative and incremental approach of the Monolith->Microservices Journey.
About Greenlight:
Greenlight is a fintech company on a mission to help parents raise financially-smart kids by providing tools to teach children about spending wisely, saving, earning, and investing.
About the Author:
Jayadeba Jena is the Head of API Platform at Greenlight.