Errant Architectures (2003)
drdobbs.com
drdobbs.com
</snark>
I am currently working on one of these much vaunted microservices based systems and, let me tell you, there is not one aspect of it - not a single one - that does not suck a perpetually replenished conveyor belt of balls. Not one.
And, yes, performance is straight up dreadful. Sadly I cannot say more in order to protect the guilty.
1. Microservices do not necessarily encapsulate storage at object level. You could certainly do that (and naive acceptance of REST may encourage you to do that), but nothing about microservice architecture makes simply exposing your business object the default approach.
2. ORBs abstracted away the distribution model. Of course, this was they main selling point: your method calls could run in a shared library, or end up being sent to an entirely different server. You could start with one and move to other at will with minimal changes to your code. Microservices (and most web APIs in general) are diametrically opposed to this abstraction. Even with auto-generated REST clients, network boundaries are very explicit, and the wire interface is never left for a middleware to decide. Designers make conscious decisions whether to use JSON-API GraphQL or gRPC, whether gzipped JSON is enough for the wired format, or should they go with Protocol Buffers, or perhaps a high performance format like Cap'n Proto or FlatBuffers. Distribution is extremely explicit.
3. ORBs generally use some sort of object pointer, which is hard to scale and even harder to use for high availability. The general assumption was that objects could be stored in memory on one server, or perhaps be backed by a database, but the client code would have no way of knowing that. So you had to obtain a reference anyway, start operating on an object, and finally committing your changes (if you had transaction support at all). This means that you could not migrate an object instance from one server to another, and you couldn't even optimize writing all 30 columns in a DB entry at once, since proper OO design was to have a separate property for each column and to set each property individually.
CORBA and COM essentially forced you into a highly stateful OO model, but microservices don't. If microservices interact with state, it is rarely managed by directly by the microservice itself, so microservice instance are entirely interchangeable. You could even implement an entirely stateless microservice.
Doing microservices right involves taking the advice in the linked article: Avoid splitting services when there is no need to; certainly avoid splitting at object/class boundaries - split out functional units that require little communication between them (ideally, split only out things that can fully serve requests from the client, and mediate state in other ways, like e.g. authentatication tokens or signed state); think about latency.
It's about refining the thinking about when it is right to split services up and how to do it without falling in the "CORBA trap" where people tried to make objects metaphorically "floating around the network" for now good reason.
Microservices, meanwhile is a reaction to ballooning applications, increasingly containing many orthogonal services / APIs, and in favour of splitting them up, with common advice seeking to avoid splitting on boundaries that will require coordination between the resulting services.
Microservices takes the consequence of the problems Fowler complains about in the linked article by trying to split applications into individual services while keeping interdependencies minimal, so that we ideally only pay the latency cost at the user<->service boundary where we have to pay it anyway, instead of all over the system, which was one of the major ongoing problems with the CORBA craze.
The one virtue of the former, non-microservice, situation is that at least you don't have the latency overhead because all that spaghetti is contained in one, or a small number of, processes, rather than spread around all over the place between a large number of processes and instances.
I'm not saying it's ideal but people who write crap software in monolithic form are only going to do a worse job when faced with the additional complexity of having it all chopped up into little pieces.
Just my two cents, and obviously not a trendy point of view.
I'd be quite interested to see how Martin Fowler's view changes over the next 11 years, assuming he doesn't retire.
If you must/want to split, split along service boundaries where communication is mostly with the client, and minimise communications surfaces between services. If you do that, you gain ability to scale services separately, and develop them separately.
If there are lots of interdependencies, you have bought yourself lots of latency and reliability issues for little gain elsewhere.
> I'm not saying it's ideal but people who write crap software in monolithic form are only going to do a worse job when faced with the additional complexity of having it all chopped up into little pieces.
I agree with that, and those people should probably start with articles like the linked one to learn to think about latency issues first.
"In short, the microservice architectural style [1] is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies."
To this:
"Distributing an application by putting different components on different nodes sounds like a good idea, but the performance cost is steep. [..] How, then, do you effectively use multiple processors? In most cases, the way to go is clustering (see “A Better Way”). Put all the classes into a single process and then run multiple copies of that process on the various nodes."
It looks like a direct contradiction. Personally, I think microservices are indeed a bit crazy and the latter is a good advice, but I am no MF.
Central authentication service that authenticates on user login and passes back and API token to be used to access other services. With something like a JWT you don't need to check the User database for every API call.
Yes, implementing all these features as microservice won't simplify the architecture, but you'll at least be able to easily migrate upgrade, remove, replace or scale up pieces of it.
IMHO, you're painting a very rosy picture here. There is a big assumption that you will want to preserve existing interfaces (REST APIs?) between those microservices. In reality, they probably will be bloated and obsolete just like everything else you have.
And being forced to update "microservice" at a time rather than as a whole, the whole process will actually become more complicated.
If the microservices has to do lots of internal communication using HTTP, then you should consider if you've split them along the right boundaries or not.
Sometimes it may not be avoidable, but if there are lots of dependencies between them, then the rationale for splitting them into separate services rapidly drops.
The two things you quote are consistent assuming the services are mostly independent on the server side and it is the client that handles the coordination.
And if this assumption ceases to hold during the life of the product? For example, I might suddenly want to do some of the client calls from within the server. What then, you scrap the microservice architecture and rebuild it as a monolith? To me this looks really contrary to YAGNI principle.
Also, I think handling coordination between internal application stuff on the client is a security can of worms. You are exposing your client to information that he would never see if everything was hidden behind one service.