We recently switched our tech stack from multiple, fully-encapsulated microservices to Apollo Federation. Each microservice was converted to a subgraph and the supergraph is federated by the gateway.
But lately it feels like we've lost some of the major pluses of working with microservices-- decoupled deployments, async inter-service communication, tons of extra wiring and time spent coordinating development to keep the supergraph stable across multiple environments.
It feels like we've regressed to a more monolithic style of working, but with independent teams and services.
Since Apollo hasn't been around for a while, and was developed against the GraphQL spec, which was initially intended to speed up Facebook's latency issues back when it was a monolith, I can't help but wonder if it's just not in a place yet to fully support microservice architectures. I sometimes wonder if it's overkill to do both Apollo Federation and microservices.
TL;DR:
Is using Apollo Federation with microservices just a bleeding-edge buzzy endeavor with diminishing returns on investment or does it make actual sense?
What are the (I imagine small number of) use cases in which doing this would make sense?
How do we retain benefits of working with pure microservices (namely, not coordinating small changes across tons of services in lockstep) in a Federation paradigm?
How do we retain benefits of working with pure microservices (namely, not coordinating small changes across tons of services in lockstep) in a Federation paradigm?
I believe that Apollo Federation is mainly useful due to the principle of separation of concerns. With this, you do not need to coordinate small changes across tons of services in lockstep. For example, say you have a Reviews subgraph and a Users subgraph. As long as the two subgraphs know the key for the other entity, they can use it without needing to coordinate changes amongst themselves. If team A implements things related to only their concern, they should ideally never have to coordinate a change with another team.
Is using Apollo Federation with microservices just a bleeding-edge buzzy endeavor with diminishing returns on investment or does it make actual sense?
If you already have microservices which are completely independent of each other, using Apollo Federation may not be the ideal tool you might want to go with. There are other open source solutions out there that would help you combine your microservices under a single gateway (such as GraphQL-mesh, Stepzen, etc.) - and these do not require your microservices to support federation. That being said, there are teams out there that still stick with Apollo Federation because of its innate ability to help separate out subgraphs in a relatively clean way though the concept of ownership separation of concerns. So there are definitely use cases (i.e. when one subgraph has to use an entity owned by another subgraph) where using Apollo Federation has an impact.
Related
After reviewing and experimenting with service mesh offerings, isn't Service Mesh a monolithic (ironic) solution for a microservice universe?
Actually, I think they are a logical side effect of a microservice universe. One of the largest drivers for moving to microservice architectures is to separate concerns such that individual services implement very specific functionality very well.
For example, a translation service should take input text in one language and convert it to output text in another language. The team writing that service should consist of generalized developers and specialized linguists. Yet, in many cases we force the developers of this business logic to concern themselves with other things (e.g., transport layer security, access control, metrics collection, announcement and discovery).
The point of a service mesh it to treat the networking of microservices as almost another microservice. It's a specialized concern that, when treated that way, allows others to focus on doing what generates revenue or solves problems.
There is a reason much of the push on microservices is coming out of companies and organizations who are pretty deep in the microservice universe at very high scale. It should also be noted that most service mesh control planes are implemented via microservices, not monoliths.
For some aspects yes, they are. Service mesh clients usually run on containers which handle (possibly) authN, authZ, aggregate logging and load balancing.
On the other side, when we move out from a monolith, we should theoretically duplicate all the business logic and authorization mechanisms. This would introduce replication and increase the maintenance effort.
Service mesh are useful because they extract these concerns out from the dev team.
I just finished reading Uncle Bob's "Clean Architecture" and now wondering how to apply it in the context of microservices!
On one hand, I think that microservices fall in the "Framework-Drivers" layer since it's an implementation on top of use-cases (they are ways to serve use-cases.) This way, we focus on the core of the app (Entities and Use-cases) and stay flexible in the implementation of the outer layers (including microservices). But since each microservice can be maintained by a different developer/team of developers, they will have a bad time when use-cases change (harder to predict who will be impacted).
On the other hand, we can split our app into multiple microservices, decoupled from each other, and apply Clean Architecture inside each microservice. The pro of this approach is that we can focus on each microservice doing one thing, and doing it well. But the problem is that we started designing using technical separations (microservices) which violates the main Clean Architecture principle of focusing on the business. Also, it will be hard to not duplicate code if two microservices uses the same entity or use-case!
I think the first scenario is the best, but I would like to have feedback from fellow developers on the long-term benefits of both scenarios, and potential troubles.
My two cents:
From Uncle Bob's words, "Micro-services are deployment option, not an architecture". Each micro-service should be deployable, maintainable by different teams (which can be in different geographical locations). Each team can choose their own architecture, programming language, tools, frameworks etc... And forcing each team to use single/same programming language or tool or architecture does not sound good. So each micro-service team must be able to pick their architecture.
How can each team code/maintain/deploy their own micro-service without conflicting with other teams code? This question brings us to how to separate micro-services. IMHO it should be separated on feature based (same principle applies to modularization of mobile application projects where independent teams should be able to work on separate modules/micro-services).
After separating micro-services, the communication between them is implementation detail. It can be done through web-socket/REST API etc... Inside each micro-service, if team decides to follow Clean Architecture, they can have multiple layers based on Clean Arch Principles (Domain/Core - Interface Adapters - Presentation/API & Data & Infrastructure). There can/will be duplicate codes on micro-services, which are OK for micro-services.
As #lww-pai-long said in his answer here splitting based on the Domain responsibilities and DDD is in most cases the best solution.
Still if you worked with a system using micro-services you soon realize that there are other things involved here as well.
DDD Bounded Context as base for micro-services
In most cases splitting your application to micro-services based on Bounded Context is the safe way to go here. From experience I would even say that in some parts of Domain you could go even further and have multiple micro-services per Bounded Context. Example would be if you have quite big part of Domain which represents one Bounded Context. Other example would be if you use CQRS for a particular Domain. Then you can end up having a Write/Domain and Views/Read micro-service.
You can read in this answer how you can split your Domain to micro-services.
It would be advisable as you said to "apply Clean Architecture inside each microservice".
Also, it will be hard to not duplicate code if two microservices uses
the same entity or use-case!
This is something that you have to deal with when working with micro-services in most cases. Duplicating code and/or data across multiple micro-service is common drawback of working with micro-services. You have to take this into account as you on the other hand get isolation and independence of the micro-service and its database. This problem can be partly solved by using shared libraries as some sort of packages. Be careful this is not the best approach for all cases. Here you can read about using common code and libraries across micro-services. Unfortunately not all advice's and principles from Uncle Bob's "Clean Architecture" can be applied when using micro-services.
Non Domain or technical operation micro-services
Usually if your solution is using micro-services you will more or less have micro-services which are not Domain specific but rather some kind of technical task's or non business operations directly. Example could be something like:
micro-service for report generation
micro-service for email generation and forwarding
micro-service for authorization/permission management
micro-service for secret management
micro-service for notification management
These are not services which you will get by splitting your solution based on DDD principles but you still need them as general solution as they could be consumed by multiple other services.
Conclusion
When working with micro-services you will most of the time have a mixture of Domain specific and Domain agnostic micro-services. I think the Clean Architecture could be looked from a little different prospective when working with micro-services.
On one hand, I think that microservices fall in the
"Framework-Drivers" layer since it's an implementation on top of
use-cases (they are ways to serve use-cases.)
It kind of does but it also falls into the other layers like Entities and Use Cases. I think it goes in the direction that if you work on Domain specific services this Diagram becomes the Architecture of each micro-service but not a concept above all micro-services. In the applications where I worked with micro-services each micro-service(the ones which are based on the DDD Bounded Context) had most of this layers if not all of them. The Domain agnostic services are an exception to this as they are not based on Domain Entities but rather on some tasks or operations like 'Create an Email', 'Create a PDF report from html template' or similar'.
I think this question may be better on Sofware Engineering but I'll answer anyway.
My approach would be to use DDD and define each microservice as a Domain Services grouping Use Cases semantically, then link Domain Services with Bounded Context.
Sam newman talk about the importance of separating microservice by domain abraction and not technical one in Building Microservices
The point he makes basically is that defining scaling strategies for microservice based on subdomain will better match the "real live" constraints observed on the production system than using technically based microservice and try to defined a abstract strategy.
And if you look at how something like Kubernetes works it seems to push to that direction. A pod end up being a microservice with multiple containers defined as a complete stack matching a sub-domain if the overhaul application.
It then gets easier in an e-commerce application, for example, to scale the Payment service independently of the Cart service based on customer activity than to scale the web services independently of the job queues in an abstract way.
The way those Bounded Contexts will communicate, i.e request based or event based, depends on the the specific relation between them. To use the same example a Cart may generate an event that will trigger the Payment, while the same Cart may need to request the Inventory before validating the order.
And at the end of a day those Domain Services* and Bounded Contexts can be implemented the same when starting with a monolith, even the Bounded Contexts communication can be. The underlying communication protocol becomes an implementation detail that can easily(kinda) be switch when transitioning to a distributed a.k.a microservices architecture.
I have a question about microservice implementation. right now I am using an api gateway to process all get request to my individual services and using kafka to handle asynchronous post put and delete request. Is this a good way of handling of handling request in a microservice architecture?
Your question is too unspecific to give a good answer. What is a good architecture totally depends on the details of your use cases. Are you serving web pages, streaming media, amass data for analysis, or something completely different? We would also need to know what are you requirements in terms of concurrency, consistency and scalability? What are the constraints for budget/size of development teams, ease of development, dev skills, etc?
For example the decisions you have taken may be considered good if you have strong requirements for a highly scalable input of large data sets and very frequent data collection as well as the team to support it. But it may be considered bad if you have a small team only and are trying to get a quick and cheap MVP for a new service that has limited scalability requirements (because the complexity of the solution slows down your development unnecessarily).
It may be good because the development team is familiar with those technologies and can effectively develop with those. Or it may be bad because your team does not know anything about those and the investment in learning those will not be justifiable by long term gains.
Don't forget that one of the ideas of the microservices architectural style is that each service can be owned by a distinct team that makes its own decisions about what technology to use for implementation (for whatever reason: ease of development, business reasons etc). So in other words the microservices style embraces the old wisdom architecture follows organization.
Here a link to a recommended further read.
As a mechanism to connect microservices together and make them work it is usually suggested to use APIs and Service Discovery. But they usually work as their own microservices, but these ones should apparently be "hard-coded" into others, as every microservice is supposed to register with them and query for other microservices' locations. Doesn't this break the idea of loose coupling, since a loss of a discovery service implies others being unable to communicate?
Pretty much yes. If one microservice "knows" about another microservice - it means that they are highly coupled. It doesn't matter where specifically this knowledge about other service is coming from: hardcoded, config file or maybe some service discovery, it still leads to high coupling.
What's important to understand is that many microservice evangelists are just preaching about how to build monolith apps on top of Web APIs. Some of them probably think that the more buzz words they use - the better ... not quite sure why this happens. It is probably easier to fake a language and become an "expert" by using buzzword salad instead of really building fault tolerant and horizontally scalable app.
But there is another way to look at service discovery: when it is used by service clients like SPA application or an API Gateway it may be very useful. Clients and gateways should know about service APIs, otherwise, the whole thing doesn't make sense. But they can use a registry to make it more flexible/dynamic.
So, to summarize:
if services are using discovery to get more information about each other - this is probably a bad thing and design flaw (pretty sure there are corner cases where this may be a valid scenario, please post a comment if you know some)
if discovery is used by other parts of the app, like SPA or API Gateway, this may be useful, but not necessarily it always is.
PS: to avoid high coupling, consider reading series of articles by Jeppe Cramon that illustrate the problem and possible solutions very nicely.
In a distributed system, you will always have some amount of coupling, what you want to do is to reduce all aspects of coupling to a minimum.
I'd argue that is does matter how you design your service location. if your code knows of the other service i.e. OrderService.Send(SubmitOrderMessage); (where 'OrderService' is an instance of the other service's proxy)
as opposed to transportAgent.Send(SubmitOrderMessage); (where 'transportAgent' is an instance of the transport's proxy i.e. the queuing service/agent and the actual address of the queue can be in your config), this reduces the coupling and your business logic code (Service) and delegates the routing to your infrastructure.
Make Sense?
Each microservice is expected to be functionally independent. For interacting with other microservices, it should rely on rest api calls only. Service Discovery plays a role to keep the services fairly loose coupled with each other. Also due to dynamic nature of the service urls, the hard dependencies are removed. Hope this helps
Reduced coupling or fairly loose coupling still has one thing in common; coupling. In my opinion, coupling to any degree will always create rigid communication patterns that are difficult to maintain and difficult to troubleshoot as a platform grows into a large distributed platform. Isn't the idea behind microservices to allow consumers to engage in "Permissionless innovation?" I would suggest that this is only possible by decomposing to microservices that have high cohesion and low coupling and then let the consumer decide how to route, orchestrate or aggregate.
I have an existing web service that supports ordering and it has multiple operations (approximately 20). This is a single webservice that support the ordering function. It interacts with multiple other services to provide ordering capability.
Since there is a lot of business functionality within this app and it is supported by a 10 member team , I believe it is a monolith (though I assume there is no hard and fast rule to define what a monolith is).
We are planning to get the application deployed in cloud foundry environment and we are planning to split the app into 2-3 microservices , primarily to enable them scale independently.
The first few apis which enable searching for a product typically have more number of hits whereas the api that support actual order submission receives less that 5% of the hits. So the product search api should have significantly larger number of instances as compared to order submission api.
Though I am not sure if we could split is based on sub-domains (which I have read should be the basis) , we are thinking of splitting them based on the call sequence as explained earlier.
I have also read that microservices should be choreographed and not orchestrated. However in order to ensure our existing consumers are not impacted , I believe we should expose a api layer which would orchestrate the calls to these microservices. Is providing an api gateway , the normal approach that is followed to ensure consumers do not end up calling multiple microservices and also provides a layer of abstraction?
This seems to be orchestration more than choreography - though I am not hung up on the theoretical aspects , I would like to understand the different solutions that are pursued for this problem statement in an enterprise world.
The Benefits of Microservices
Deploy & Scale Independently
Easier to 'Reason About'
Separation of Concerns
Single Responsibility
(Micro)Service-Oriented Architecture
I would suggest splitting your services based on domain. This is a logical and efficient approach which makes it an easy starting point. Your monolithic package structure may already be organized in this manner, which simplifies the refactoring even more.
API Gateway
The typical Spring Cloud approach for this would be to use a Zuul Proxy on the edge of your network which receives the requests from your clients (web, mobile, etc.) and routes them to the microservices located behind your firewall. The client only interfaces with a single domain, and it handles CORS out of the box.
Resources:
API Gateway Pattern
Routing and Filtering