News for hackers. Stuff that matters.
Re:Starting with microservices by scanr on Wednesday January 19, 2022 @04:28PM (#30000412)
I have a theory that auto-wired Dependency Injection in a single DI container is partly to blame for monolith spaghetti. Once an app reaches a certain size and anything can depend on anything else, reasoning about the whole can become difficult.

I think there is value in wiring a monolith together in such a way that each course grained subcomponent exposes a constrained interface into the rest of the system (payments, orders, shipping, customers etc) before needing to break it into distributed micro-services.

Note: I quite like Dependency Injection, I just think the 1 giant bag of dependencies can lead to complexity at scale.

Re:Starting with microservices by malort on Wednesday January 19, 2022 @04:41PM (#30000596)
This is exactly what Shopify does with their monolithic Rails app [1]. I worked at a company with ~200 engineers that used the same general architecture and I really enjoyed it. We got a lot of the benefits (clear interfaces, teams able to work on their system without having to understand the whole platform, build optimization, etc) without any of the operational headaches that come with microservices.

[1]: https://shopify.engineering/shopify-monolith

Re:Starting with microservices by lmilcin on Wednesday January 19, 2022 @04:41PM (#30000595)
I wouldn't blame (badly implemented) DI for all the problems of monoliths.

I think the real main issue is lack of discipline when dividing the application into modules.

Spaghetti is basically defined as an application where real modularisation does not exist and everything talks to everything.

It is much easier to work with an application when you can abstract parts of it when you are solving your problem. You effectively work on much smaller part of the application.

Spaghetti == you effectively have to take into account possibility of the piece of code you look at interacting with any other piece of code in the application.

Well modularised application == you only need to take into account the contents of your current module and the interface of the other modules you are using.

One reason why microservices sort of work (when done well) is because they force people to think about APIs and how those services talk to each other.

In most cases you could just put these microservices as modules in a monolithic application and expend the effort on ensuring APIs and application structure.

I have successfully rolled back couple microservice initiatives by integrating services into monoliths. This usually results in the team getting back a lot of their time because their environment suddenly became much simpler. Less applications to manage, less network communication, less possible ways for things to break, less frameworks, less resources needed to run the application, less processes (like processes around deployment, release management, etc.), less boilerplate code, and so on. The list is very long.

Of course, when you work on a large monolith vs a lot of small microservices, it is now important to be able to structure your applications. But there is also an opportunity for improvement.

Re:Starting with microservices by jstimpfle on Wednesday January 19, 2022 @05:39PM (#30001445)
Ravioli == you have so many small distinct things on that are hard to stick together, which makes it hard to build a larger structure out of them.
Re:Starting with microservices by lmilcin on Wednesday January 19, 2022 @05:58PM (#30001670)
Then try to not make the modules too small?

Sure, if you were ready to make an entire separate application for it it will not be worse when you turn it into one package of a monolith?

Also you can build nested structures with modules (like a larger module consisting of smaller submodules).

Divide and conquer. This is how abstraction works in a nutshell. You start with a large problem, divide it into smaller parts (modules), each part you treat as a separate problem dividing it into smaller modules, and so on.

There exists no need to have a flat application consisting of hundreds of sibling modules.

An application might consist of larger modules, then some of those might have smaller submodules (packages), then those might have even smaller ones (classes), then you have methods, then you have statements, then you have function calls, etc.

A large monolith does not have to succumb to spaghetti or ravioli-type structuring. A good developer should know how to structure an application of any size.

Re:Starting with microservices by throwaway894345 on Wednesday January 19, 2022 @05:52PM (#30001589)
I’ve never been part of a monolith that used a DI framework, but I’ve seen quite a few monoliths fail (as in “the project becomes too convoluted and iteration slows to a crawl until the project is canceled or effectively rewritten”) and I certainly believe that one important reason microservices do well is that they enforce the modularity that you describe. That said, a lot of critics of microservices describe similar issues of indiscernible chaos, so either I’ve been very fortunate or microservice critics are gaslighting us. :)
Re:Starting with microservices by BatteryMountain on Thursday January 20, 2022 @02:17AM (#30005773)
At that point it becomes useful to look at some indirection/decoupling patterns like the Mediator pattern.

Then instead of having a IService with 15 methods with a construction with 20 dependencies listed (multiply it with say 50 IServices in your project - mmmm spagheti project), you end up with a IHandler with a single method and a constructor that only has say 3 injected dependencies, only what it needs. The trade off is, you now have 100's of small Handler files, some people don't like that BUT you now have a pleasant git commit history per file and most of the code of a Handler file fits on one screen and it is easy to digest. You can also let a handler trigger other handlers if it needs to (via the same mediator). It also fits with SRP.

Auto-wiring 95% of your dependencies still stays intact as the above plumbing will need it to work.

Re:Starting with microservices by ceesharp on Thursday January 20, 2022 @04:10AM (#30006571)
If your IService has 15 methods and your ctor has 20 dependencies injected, then you have other issues. :)
Re:Starting with microservices by rzzzt on Wednesday January 19, 2022 @06:45PM (#30002133)
There was another article about building this structure into a single, self-contained executable and decide via command line arguments which piece(s) to use at startup. For some reason I remember "microlith", but it must be another clever word combination because I can not find any relevant HN posts, just one about archaeology...

You can run a single copy of the resulting binary (eg. for testing) that spins up all sub-components, or copy it to multiple machines and start individual parts. The ones which happen to run together can use in-process communication as well, others will have to dial in via remoting.

Re:Starting with microservices by jdlshore on Thursday January 20, 2022 @01:49AM (#30005624)
I’ve coined the term “microlith,” but I’m probably not the only one and it may not be what you’re thinking of. I wrote about it in the new edition of my book, and also discussed it here: https://www.jamesshore.com/v2/projects/lunch-and-learn/micro...
Re:Starting with microservices by 4rb1t on Wednesday January 19, 2022 @05:51PM (#30001583)
In theory it all sounds great and makes total sense but when the company grows to a billion dollar firm and hires engineers to work on the said monolith thats when things breakdown. The founding team was a closet knit team and ensure there is no spaghetti code but the moment you move on from that closely knit group its hard to enforce constraints.

Have worked at multiple companies that started out as a monolith and are still running the monolith in some form or the other while breaking it down into micro services.

Re:Starting with microservices by mcv on Wednesday January 19, 2022 @05:30PM (#30001319)
I notice there's a lot of comments here saying this exact same thing, and it's also what seems most sensible to me. Yet microservices are getting all the hype. Should be hype a thorough modular design more?
Re:Starting with microservices by heavyset_go on Wednesday January 19, 2022 @06:19PM (#30001903)
Yes, but microservices maximize cloud vendors' revenue compared to monoliths.
Re:Starting with microservices by Nextgrid on Thursday January 20, 2022 @06:17PM (#30016781)
They also maximize the number of engineers and managers needed to oversee the whole thing. People are unlikely to speak against something that guarantees their employment.
Re:Starting with microservices by truffdog on Wednesday January 19, 2022 @11:46PM (#30004780)
The nice thing about microservices is that you don't need iron discipline to maintain modular boundaries, instead its just how things work.

I like to analogize it to assembly vs structured programming languages, or C vs Java- you can write the same programs in either, you can write bugs in either, but there are whole classes of pitfalls that get erased by moving from one language to another.

Re:Starting with microservices by yakshaving_jgt on Thursday January 20, 2022 @03:31AM (#30006313)
That’s not true. People in industry commonly refer to the consequence of their misguided decision to embark on a microservices adventure as a “distributed monolith”.
Re:Starting with microservices by loevborg on Wednesday January 19, 2022 @05:39PM (#30001442)
Check out Polylith for a description of this great pattern https://polylith.gitbook.io/polylith/
Re:Starting with microservices by bob1029 on Wednesday January 19, 2022 @05:43PM (#30001502)
> Once an app reaches a certain size and anything can depend on anything else, reasoning about the whole can become difficult.

I have experienced this pain so many times and in so many different varieties. Often, you cannot meaningfully subdivide the problem space without ruining the logical semantics per the business (i.e. bowl of spaghetti).

An alternative is to embrace the reality that circular dependencies are actually inevitable and appropriate ways to model many things.

The example scenario I like to use is that of a typical banking customer. One customer likely has a checking and savings account. For each of those accounts, there are potentially multiple customers (joint ownership). Neither of these logical business types will ever "win" in the real world DI graph. Certainly you can start to invent bullshit like AccountCustomer and CustomerAccount, but that only gets you 1 layer deeper into an infinitely-recursive rabbit hole of pain and suffering. There also exists the relational path, but I have heavily advocated for that elsewhere and it is not always applicable when talking about code-time BL method implementation. Being able to model things just as they are in the real world is a big key to success in the more complicated problem domains.

Instead of trying to control what depends on what, I shifted my thinking to:

> What needs to start up and in what order?

Turns out, most things don't really care in practice. The only thing I have to explicitly initialize before everything else in my current code base is my domain model working set (recovery from snapshot/event logs) and settings. I decided to not use DI for any business services. Instead, all services become a static class with static members that can be invoked from anywhere. This also includes the domain model instance which is used as the in-memory working set. This type just contains an array of every subordinate domain type (Customers, Accounts, etc.). By having the working set available as a public static type, every service can directly access it without requiring method injection. If I was working with a different problem domain (or certain bounded context within this one), I might prefer method-level injection.

Yes - according to every book on programming style you ever read, this is an abominable practice. Unit testing this would be difficult/impossible. But you know what? It works. It's simple. I can teach a novice how to add a new service in an hour. A project manager stumbling into AccountService might actually walk away enlightened. You can circularly-reference things at runtime if you need to. I've got some call graphs that bounce back and forth between customer & account services 5+ times. And it totally makes sense to model it that way too as far as the business is concerned. Everyone is happy.

Re:Starting with microservices by tmstieff on Thursday January 20, 2022 @04:33AM (#30006721)
I think I agree with you as well. Although it is hard for me to picture exactly how you've structured your dependency graph. In any case, extracting the business logic into some sort of static method / class has definitely been one of the only useful things I can carry across projects that works in nearly all use cases. It also makes unit testing the actual business logic extremely easy. That said, you can end up with a static method that takes 20 parameters, which is always fun. But, in those cases, you are lefty dealing with complexity that is intrinsic to the business, rather than complexity introduced through some bad architectural decision, so at least it is isolated.
Re:Starting with microservices by bob1029 on Thursday January 20, 2022 @06:09AM (#30007380)
> That said, you can end up with a static method that takes 20 parameters, which is always fun.

I used to get upset about this, but now I embrace it. If a business method requires certain things to operate, then modeling those things as arguments to the method is totally reasonable. Some business is complicated and messy so we would expect more arguments to be involved. Trying to sweep reality under the rug just makes things 10x harder elsewhere.