[HN Gopher] Starting with microservices ___________________________________________________________________ Starting with microservices Author : feross Score : 70 points Date : 2022-01-19 20:03 UTC (2 hours ago) (HTM) web link (arnoldgalovics.com) (TXT) w3m dump (arnoldgalovics.com) | yawnxyz wrote: | I don't understand why people treat microservices or monoliths as | either or. If I find my various projects using the same | functionality over and over, then I'll split that off as a | microservice to serve my monolith projects of various sizes. | | It's like functions: don't turn code into a function until you | need it in three different places. | galovics wrote: | I agree but the trend still seems to prefer one or the other | and that's why it's so important to talk about the potential | downsides instead of closing our eyes and saying everything is | gonna be better if we do microservices. | foobarian wrote: | Maybe it's just the activation energy. Until you have the setup | for making a microservice, you're kinda limited to a monolith. | Once you pay the setup cost though, making incrementally more | services is trivial so you end up on the other end of the | spectrum. | scanr wrote: | 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. | loevborg wrote: | Check out Polylith for a description of this great pattern | https://polylith.gitbook.io/polylith/ | bob1029 wrote: | > 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. | mcv wrote: | 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? | 4rb1t wrote: | 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. | throwaway894345 wrote: | 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. :) | lmilcin wrote: | 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. | jstimpfle wrote: | 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. | lmilcin wrote: | 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. | malort wrote: | 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 | kosolam wrote: | The author needs to express itself more succinctly, maybe it will | help also with his architecture struggles | marcosdumay wrote: | Do you know what kind of software has an absurd level of | horizontal scalability by default? Web servers. | | The idea of splitting your web servers into multi-tiered web | servers for scalability is, well, weird. Yet, somehow it's the | main reason people keep pushing it. Even this article repeats | this. | | There's nothing on microservices that adds scalability. They make | it convenient to deal with data partitioning, but it's an | ergonomics change, not one of capability. | monocasa wrote: | I agree with you for the most part, but to be devil's advocate, | I believe the argument is that the data stores don't tend to | horizontally scale the same way except in the most 'cdn' like | data flows. | delusional wrote: | > but it's an ergonomics change, not one of capability. | | Isn't that a bit of an empty argument. Literally anything | Turing complete is an ergonomics change when compared to | anything else Turing complete. | | We are writing for the same computer. There's nothing different | (fundamentally) about the kernel and my program. | smrtinsert wrote: | It's scalability with regard to parallelism if work streams and | business units. One module can respond to market faster if all | the other business units and teams don't have to weigh in | JamesSwift wrote: | It seems over time some nuance has been lost in translation on | this. Microservices weren't 'more scalable' than monoliths, | they were 'more appropriately scalable'. In other words, you | could scale parts independently and shape your infrastructure | more effectively for your workload. e.g. If your bottleneck is | logins, you scale the LoginService and don't need extra copies | of the AppointmentService running to keep up. | aflag wrote: | Sure, but I think the point is that it's unlikely the | bottleneck will be in the web server, but in whatever | database you're using for your logins. Just because you have | a single program, it doesn't mean it is required to use a | single database. | | But even if the webserver is the problem, it feels unlikely | that you'll end up saving much in infrastructure cost by | scaling just the login service rather than just deploying | more copies of your webserver. | | Not saying it is impossible, but it's unlikely for that to be | a good justification to adopt microservices. I'm not saying | that there aren't good reasons to use microservices, but I | agree that scalability is not a good selling point. I'd | actually argue that it's harder to build scalable | microservices than monoliths. | antihero wrote: | I guess the idea is to split the workload into parts so if one | part of the workload is proving to use more resources, it can | then get a larger dedicated pool of resources. | | Plus being able to choose different tech for different types of | work, and also being able to split between teams when your org | gets more vast. | | Though I guess this is more about using services than micro | services, per-say. | Nextgrid wrote: | I never got the "scalability" argument of microservices. You | can trivially deploy multiple instances of your monolithic web | application - chances are you're already doing so by running | multiple workers/threads in your application server. Spreading | that to other machines is trivial. | | The real issue is in scaling the data store. Microservices | typically work around that problem by each having their own | separate database, but nothing prevents your monolith from also | talking to different DBs. | baash05 wrote: | Totally.. and adding read replicas in the mix too. Even rails | allows you to stipulate the DB each model lives in, in a | rather trivial manner. | pas wrote: | And there are DB proxies that can hide this complexity from | the application (to a degree). | pas wrote: | Monolith becomes a problem when it becomes too big to | build/test/deploy/debug in a sane fashion. | | And when you have alternatives, great. But for example game | devs don't. Or operating system devs. Though of course these | are all active areas of ongoing research (for decades!). | | If the problem/subject were that easy we would have already | solved it and it wouldn't be a hot topic. | | And since it's likely a nonlinear problem it's hard to map | the problem-solution space, hard to get a good mental model | for it, so we use the next best thing: stories. We have | success stories and parables on what not to do. (And whole | conferences dedicated to telling them :)) | marcosdumay wrote: | Building, ok, but for testing or debugging microservices | aren't really an alternative. Just like scalability, they | add no new capability here. The most they can do is adapt | better to some set of procedures than a monolith. | | For deploying they are nothing but a very large hindrance. | a45a33s wrote: | i think everything is already covered with this video | https://www.youtube.com/watch?v=y8OnoxKotPQ | jiggawatts wrote: | Architecture astronauts _love_ microservices. | | I'm watching a government customer take simple, cohesive systems | developed by a small team (4-5 people) and _split it up_ into | tiny little pieces scattered across different hosting platforms | and even clouds. | | Why? | | Because it's "fun" for the architects and pads out their resume. | | Just now, I'm watching a "digital interactions" project that will | have dozens of components across two clouds, including multiple | ETL steps. In all seriousness, half of that could be replaced by | a _database index_ , and the other half with a trigger. | | They're seriously going to deploy clusters of stuff on Kubernetes | for 100 MB of data to make sure it "scales"... to 200 MB. Maybe. | Eventually. | | What kills me is that now that they've made the decision to over- | engineer the thing, my consultancy firm can't bid on the tender | because we don't have the appropriate experience building over- | engineered monstrosities! | | The simple and effective solutions to problems we've delivered in | the past are _disqualifying_ us from work. | | You guessed it: my next project will be an architect's wet dream | and will be over engineered just so that we can say on tender | applications that "yes, we have the relevant experience". | | Gotta play the game by the rules... | AtNightWeCode wrote: | noob | marginalia_nu wrote: | Lifecycle management is also a big part of it, I think. | | My search engine is a hybrid architecture, with some monoliths | and some microservices. | | * Index - 5+ minute start time and uses 60% of system RAM.; | | * Search Server (query parser, etc.) - 30 second start time due | to having to load a bunch of term frequency data, low resources, | ephemeral state | | * Assistant Server (dictionary lookups etc.) - fast start, medium | resources, stateless | | * Crawler - only runs sometimes, high resources, statefull | | * Archive Server- fast start, low resources, ephemeral state | | * Public API gateway - 5 second start time, low resources, | ephemeral state | | + a few others | | A lot of the ways it's divided is along the lines of minimizing | disruption when deploying changes. I don't have the server | resources to run secondary instances of any of these, so I'm | working with what I've got. If I patch the crawler, I don't want | the search function to go down. If I patch the search function, I | don't want to have to wait 5 minutes to restart the index. | | It would certainly be cleaner to say break apart the Index | service in terms of design, as it does several disparate things, | but those things have a resource synergy which means I can't, not | without buying another server and running a small 100 GbE network | between them. Seems silly for a living room operation. | teekert wrote: | We have a big codebase where I work and it's written as a set of | micro services. I'm a bit of a noob but I thought that micro | services were always containerized. Turns out we don't do that | and it all runs in a very bespoke and poorly documented VM. Now | can we call this a microservices based architecture? I feel like | we cannot, it feels like worst of both worlds... | monocasa wrote: | I've come to the conclusion that microservices as usually | implemented are simply a Conway's law concern. | | We haven't had a great track record defining the semantics of | component boundaries, but http is both simple enough that it | constricts code flow in a particular way leaving a lot of | misunderstandings and bike shedding discussions off of the table, | and is the ligua franca for external services owned by other orgs | so your engineers can be universally expected to understand those | semantics. There is a nice decoupling of letting teams make | technology choices though. Letting that drift on a per team basis | is kind of nice to allow experimentation with different | technologies without having to get the whole org onboard. | | Maybe I need to go on the talk giving, consultant circuit | reselling a restricted view of DI as "hybrid service | architecture! Write your code as microservices but DevOps can | colocate your apps in the same process if that's more efficient!" | Lolz | | It's also quite a nerd trap as we developers love to separate | components into as small of boxes as possible, but just like I've | seen super deep object hierarchies that didn't gain you anything | at the end of the day, it's easy to fall into the trap of | spinning every tiny bit of code into its own service. | | I'm creating my own backend arch right now from scratch, and | really trying to look at each network traversal from a "what does | drawing this boundary actually get me" perspective. And I'm only | splitting that out because the third party protocol being | implemented is inherently separate services in an interesting | way. | [deleted] | bob1029 wrote: | I have done the full theme park ride on | monolith->microservices->monolith. Both have ups and downs. | | The most important thing I learned is that "microservices" in | absolutely no way necessitates "bullshit spread across multiple | cloud vendors and other scenarios involving more than 1 | computer". What part of microservices says things _must_ be | separated by way of an arbitrary wire protocol? | | We now have a "monolith" (process) that is comprised of many | "microservices" (class files), each of which is responsible for | its own database, business logic, types, etc. These services are | able to communicate amongst themselves using the simple path of | direct method invocation. | | If you are looking to scale up your capacity or even make things | more resilient, microservices vs monolith is really not the | conversation you need to be having. If you are trying to better | organize your codebase, you might be in the right place but you | also need to be super careful with how you proceed. | | We wasted a solid 3 years trying to make microservices solve all | of our problems. Looking back, we spent more time worrying about | trying to put Humpty Dumpty back together again with JSON APIs | and other technological duct tape than what our customers were | complaining about. | jayd16 wrote: | Farming out libraries or subprojects to separate teams is an ok | way to go as long as you don't mind a single language/build | system. Its not as flexible but you maybe get some perf out of | it. | | You don't get the convenience of a single datastore with simple | transactions but it sounds like you prefer the flexibility of | services owning their own data. | | As it turns out there's no silver bullet. Do what works for | you. | frozenport wrote: | >> single language/build system | | Imagine designing your entire architecture around your build | system. | brundolf wrote: | You could even have a mono-repo that holds services which run | as separate processes, if you need that. That way you still get | the benefits of sharing code, types, version numbers, build | processes, etc, which seem like the main headaches with the | usual approach. | bob1029 wrote: | We passed right by this mode of operation while migrating | from microservices=>monolith. Everything is a separate | process but running on the same box. Ran that way for quite | some time. We pulled services into the main process one-by- | one. It went about as smoothly as one could have hoped for. | rapind wrote: | Pretty sure microservices will be remembered as a horribly | convoluted stop gap once we have better language | modularization. | | They encourage modular patterns which is usually good, but all | that plumbing will eventually become unnecessary. I can't help | but remember building 6 versions of each class in the old ejb | days whenever I hear microservices hype. | antishatter wrote: | Pretty clearly the pros and cons come down to the application | and necessary stability. Need ultra stable? More and smaller | microservices usually better. Need fairly stable? Bigger | services is fine. Need it to work generally? Build it however | you can. | withinboredom wrote: | Currently at my day job, we have a monolithic code base with | multiple entry points. If you update a library, you have to | update the client or keep the changes backwards compatible. I | really prefer this over multiple micro services. | speed_spread wrote: | TL;DR - don't. (which I agree with) | m4l3x wrote: | In my opinion implementing strong interfaces and good | modularization is something, that we should talk about more, than | doing Microservices. In the end it might be easy to rip of a | Microservice, when needed, if the code is well structured. | fatnoah wrote: | > In my opinion implementing strong interfaces and good | modularization is something, that we should talk about more, | than doing Microservices. | | This is my position as well. Strong interfaces, good logical | separation of function under the covers, etc. should allow | splitting off things at whatever level of micro you prefer. | alexvoda wrote: | Indeed, the principal seems to often be forgotten. The why of | monolith vs citadel vs microservices is ignored by some people. | | This results in K8s-driven-development instead of | microservices. | baash05 wrote: | I love that sentence. | jerf wrote: | This is what I do in practice. I've seen it called a | "distributed monolith". | | One of the good reasons to spend time with Erlang or Elixir is | it'll _force_ you to learn how to write your programs with a | variety of actors. Actors are generally easy cut points for | turning into microservices if necessary. As with many | programming languages I appreciate not being _forced_ to use | that paradigm everywhere, but it 's great to be forced to do it | for a while to learn how, so you can more comfortably adapt the | paradigm elsewhere. My Go code is not 100% split into actors | everywhere, but it definitely has actors embedded into it where | useful, and even on my small team I can cite multiple cases | where "actors" got pulled out into microservices. It wasn't | "trivial", but it was relatively easy. | ravi-delia wrote: | That's where a lot of the fun of Elixir comes from I think. | It's viscerally satisfying to split off functionality into | what almost feels like an independent machine that happens to | be in the same codebase. It clicked in a way normal object | oriented programming never did for me, I guess since it's not | feasible to mint 10,000 genservers to use as more complicated | structs. | cultofmetatron wrote: | haha I was thinking the same thing. The code base of my | startup is a monolith but in reality its a fork on request | webserver sending messages to a collection of genservers | actings as services. | | In essence, creating a microservice with elixir is about | the same amount of effort as adding a controller in rails. | baash05 wrote: | Yeah.. rails hits this same nail with jobs.. I use jobs in | a synch manner, and it's great. It's a rather functional | way of working within an OO world. | guntars wrote: | There's something to be said about the benefits of having a | serialization boundary between parts of the code that's a natural | consequence of microservices, but it's something that could be | employed in a monolith just as well. Then if at any point you | actually need to split out a module into its own process or | machine, it would be trivial to do. | noizejoy wrote: | I have no idea how much sense it makes to have this discussion | without a reasonably specific underlying business context. | dmitriid wrote: | > Starting a greenfield project with microservices is easy. | | That's an overstatement if I've ever heard one. Staring a | greenfield project with microservices is a nightmare. In a | greenfield monolith you can code and deploy your dependencies | together. In greenfield microservices? Oh, right. Set them all up | for deployment, figure out netwroking and access between them, | any change requires the fix-test-deploy dance etc. ___________________________________________________________________ (page generated 2022-01-19 23:00 UTC)