Functional Decomposition and Service Organization in a Multi-Granular Service-Based Architecture

Gokhan Mansuroglu
Garanti BBVA Teknoloji
7 min readApr 27, 2021

--

When you decide to move to a modern architecture there are several key decision points. While there are relatively simple and straightforward answers for greenfield companies, the answers get varied and complicated as the size of an enterprise gets bigger. Furthermore, large businesses will often need to use more than one option together.
One of the most important decisions you have to make is the new architectural style you should apply. For some time, the short answer has been microservice architecture, mainly due to the demand to gain agility and leverage cloud computing. However, for various reasons, in a banking environment, it is inevitable to have a hybrid style of service-based architecture, possibly consisting of micro, mini and macro services. Micro-service architecture can be thought of as a subset of service-based architecture, but apart from service granularities, there are some fundamental differences. However, this article does not aim to explain these architectures in detail but to discuss a practical approach to service architecture decisions in large-scale enterprises.

Functional Decomposition

Designing relatively small services built around business capabilities¹ guides engineers to Domain Driven Design principles. The first model that comes to mind is the Decompose by Business Capability Pattern. Here, the motivation is to define the boundary of a service according to the Single Responsibility Principle. With this approach, it seems appropriate to have one service per capability.

Fortunately, having a capability model or a function map is a relatively common architectural practice in the banking industry, which is an excellent starting point for identifying potential service candidates. If you don’t have it, you should start creating such a function map at first hand (you could make use of BIAN Service Landscape). This map should clearly define the components of the banking business as domains and subdomains. But never forget, this is the problem space, as engineers we need to focus on the solution space.

Bounded Context is the magical word for your solution space. Beyond its widely known definitions that emphasize domain models which reflect a ubiquitous language, the bounded context defines the logical boundaries of the components owned by a single team to provide a set of related capabilities. For relatively small businesses, the bounded context could be equivalent to subdomains. However, you may need to dig a level deeper in banking business. In practice, bounded context is the product and ideally, a team owns the development, maintenance and operation. Many articles consider the bounded context as a service, but this approach doesn’t seem to work in complex business domains.

Inside bounded contexts, you need to investigate and specify the business capabilities or in other terms the functional units. A capability or functional unit is a group of highly cohesive functions that belong to a specific domain entity or business flow you provide to create a business value. This is the finest level functional decomposition and is expected to be the functional boundary of a service in a micro-service architecture.

As a result of this decomposition what you will find for a particular functional unit should be similar to the following diagram:

5-Level Functional Decomposition Example. All top-down relations are 1-n.

In this example, Limit Management becomes your product and Limit Allocation becomes one of your services.

Be Conscious of Your Transformation Strategy

Martin Fowler recommends the Monolith First approach as seen below:

https://martinfowler.com/bliki/MonolithFirst.html

Definitely, monolith first is the best alternative for greenfield companies. On the other hand, companies with strong business backgrounds and long years of technology capabilities, such as well-established banks, are much more advantageous in defining the functional organization of their business. This is the power to define the current. But moving to a microservice architecture is not only about a technological transformation but also about organizations, business structures and processes. This is the future and the future is far beyond the current ! So, undoubtedly, there will be foggy domains. So, in case where you are able to clearly define the functional units, go with micro-first, otherwise follow what Sam Newman offers :

Start out more coarse-grained and move to fine-grained as you learn more about the service.³

No matter which strategy you choose, re-architecting and refactoring will be the natural part of your daily life. Just be aware of what you are doing !

Technical Difficulties

You have decomposed your business into the finest level which sounds pretty good and ideally, you are going to implement separate services for each functional unit. And for sure, you previously set up a container orchestration platform, created your delivery pipelines, established a centralized logging and monitoring system, and met many other microservice architecture requirements. So, let the next challenges come :

  • Distributed Data Management: Often, many business operations require collaboration of multiple services that manage their own data independently (look at Data Consistency in Microservices Architecture for a detailed explanation of the problem). In some cases, particularly in financial transactions, the business cannot tolerate any inconsistency or latency which make useless of any eventual consistency solution. For such situations that require atomic transaction, Transaction Script pattern is introduced. Transaction script organizes business logic by procedures where each procedure handles a single request from the presentation.⁴ Nevertheless, designing services per transaction is not an effective way as they are not sustainable. Instead, you may design mini-services that combine multiple related functional units. Note that, this is only applicable for services that access the same database.
  • Performance and Reliability: Despite the optimized container platforms, service communications are not costless. Each service invocation comes with a roundtrip latency and if you have dozens of inter-service communications, this will definitely affect the service level agreements. Naturally, you should reduce the latency to an acceptable level. But apart from the performance effect, too many inter-communication may be a sign of closely related functions. So, do not feel lazy to review your decomposition process in this case. Additionally, there is a risk of failure in each network traffic. The probability of this risk may affect the overall reliability of the system. Sometimes, too much inter-service communication is an indication of Grains of Sand Pitfall. This means you divide the functionality into the size of a grain of sand. So, be careful about this pitfall and do not underestimate the importance of combining those related tiny units. Additionally, don’t forget that size of the functional units, which reflects the interface size, will vary from small to large as well, but you should keep it at a level that can be maintainable.

Service Organization

As discussed earlier, functional units are usually related to either business entities or business flows. These are implemented in two different types of services; business and composition services.

  • Business service: It is the functional component that directly accesses its data and enforces business rules, thereby providing reusable business functions. In case of a highly data-centric approach, you may think of separating data access as data services.
  • Composition service: This component is mainly responsible for the orchestration of the business services based on a business flow and data aggregation. Additionally, firing domain events and data caching is performed mainly by composition services.

Apart from the business services, you need a few more components to publish your functionalities to your channels and consume external resources :

  • General-purpose API gateway: API gateway provides channel-agnostic general-purpose APIs and is the unified entry point of consumer channels. The gateway’s main responsibility is to enforce corporate-level policies such as security and auditing.
  • Channel service: While providing channel-agnostic APIs, you need to consider the different requirements of various channels. Channel services are responsible for any channel logic and data mapping which are actually an implementation of BFF⁵ pattern. In order to provide functionalities that cover various channels, channel services will require a multi-channel infrastructure that supports channel-specific functionalities. This will let the channel service to be a product-level API enforcement point. You may prefer to design a channel service per channel as well as one channel service for your product as an extension of your API layer.
  • Integration service: Integration services are the implementation of the anti-corruption layer pattern. These are used for maintaining access to external resources and constitute an isolation layer that protects your new system from the external system’s corruption effects

Considering what we have discussed so far, the proposed service organization should look like as below :

Conclusion

Based on what you really want to achieve, your architecture decisions will vary. In fact, what you really want is also not uniform across your products and applications. What’s more, your needs continue to change. So, more important than designing a well-thought-out architecture is to keep the architectural process alive and let it accompany your journey.

[1]: Microservices. https://martinfowler.com/articles/microservices.html

[2]: Decompose by Business Capability Pattern. https://microservices.io/patterns/decomposition/decompose-by-business-capability.html

[3]: Microservices antipatterns and pitfalls. https://www.oreilly.com/content/microservices-antipatterns-and-pitfalls/

[4]: Transaction Script. https://martinfowler.com/eaaCatalog/transactionScript.html

[5]: Pattern: Backends For Frontends https://samnewman.io/patterns/architectural/bff/

--

--