8 ms·
I hate to be the guy who says "you aren't doing services right," but... you aren't doing services right. > Each service has to be maintained and deployed and i
by mjr00 3y ago
I hate to be the guy who says "you aren't doing services right," but... you aren't doing services right.
> Each service has to be maintained and deployed and increases the boilerplate needed for shared data structures, complicated communication protocols and infrastructure. By the time we released our app, the common library used by most services has been updated over a hundred times. And every service has to follow suit eventually.
Shared data structures? Common library? Services have an API. If you are sharing data structures and a common "core" library across services, yes, you are officially doing it wrong. From the architecture diagram it also looks like there's only one MongoDB instance, which, if you have multiple services talking to the same database[0], is also a major sign that you're doing it wrong.
IMO the author came to the correct conclusion, that monoliths are better for a new project, but for the wrong reasons. Or maybe the right reasons which aren't clearly explained, which is that at the beginning you don't have a good enough grasp of the domain to properly separate into independent services.
[0] yes, there are exceptions where you can have multiple services use the same physical database to reduce maintenance overhead, but enforce separate ownership through different logical databases or even table-level permissions, but I'm assuming that wasn't done here.
- cbuu 3y agoAbout the MongoDB instance: It's indeed one physical instance but each service owns its own database. Thank you, I will make that more clear.
- beembeem 3y agoIt's a replicaset at least, correct? Hopefully not standalone
- tmshkr 3y agoWhat is wrong with sharing a single database between services? Seems like a pretty commonly accepted pattern: https://docs.aws.amazon.com/prescriptive-guidance/latest/modernization-data-persistence/shared-database.html https://docs.aws.amazon.com/prescriptive-guidance/latest/mod...
- deleted 3y ago[deleted]
- mjr00 3y agoIn the first paragraph, that doc echoes exactly what I explain. > You need to carefully assess the application architecture before adopting this pattern, and make sure that you avoid hot tables (single tables that are shared among multiple microservices). Using the same database table with multiple services (micro or not) is an absolute disaster.
- lukeasrodgers 3y agoHaving service A write to a table and service B read from that table in the same database is a perfectly suitable architecture for many applications.
- mason55 3y agoAt that point it's just an argument about "what is a service"? But people typically find that using the database as an API ends up with the services so coupled that they might as well be one even if they technically run as different binaries.
- tmshkr 3y agoExactly. We’re talking about software architecture for startups here. Best to keep it simple.
- Scarblac 3y agoIsn't the database just another service? Are other services also restricted in the same way?
- lucasyvas 3y agoIt's nuanced, but it's not because there is no single owner. If microservices share a database, it's not clear who migrates the schema and coordinating it is nearly impossible because another piece of code in another service may be reading the old schema and break. Sharing the database means you have a distributed monolith - you lose all independent deployability. It is the worst of both worlds. The exception may be if you share a database instance, but each service has its own schema and only has authorization to read and write to its own schema. That doesn't make the single database a service - it would make it shared infrastructure, however. This may bring its own maintainenance challenges but it doesn't violate any principles.
- deleted 3y ago[deleted]
- episteme 3y agoThe request/response objects for the API are a shared data structure, whether you decide to share them via a library or not, no?
- rcme 3y ago> Shared data structures? Common library? Services have an API. If you are sharing data structures and a common "core" library across services, yes, you are officially doing it wrong. From the architecture diagram it also looks like there's only one MongoDB instance, which, if you have multiple services talking to the same database[0], is also a major sign that you're doing it wrong. Yes, and that API has types, e.g. protocol buffers. And also, sometimes you just want to change the API, and you want to make that as easy as possible.
- preseinger 3y agoAPIs defined with IDLs like protobufs make sense sometimes, but not always using an IDL means that consumers of your API need to depend on an IDL definition at the source level, which introduces coupling that is not always useful for example, talking to the stripe API doesn't require you to import a stripe.proto
- BoppreH 3y ago> Shared data structures? Common library? Services have an API. If you are sharing data structures and a common "core" library across services, yes, you are officially doing it wrong. Maybe I've been doing this wrong all my life then. Imagine I want to fetch user data by id from Galactus, the user data service. I know what shape of data I want from it, and Galactus knows what shape of data it provides. Should both my service and Galactus have full, individual copies of this data structure? Alternatively, if Galactus has a published schema that I refer to, isn't that a shared data structure? By extracting the user data shape to a common repository (library, header, schema, whatever), not only we deduplicate code, but we also centralize documentation. Maybe the `address` field is cached and frequently outdated, and someone from Galactus noted this down along with where to find the golden source. Once you factor in other sources of redundancies (username regex, recommended logging, business-specific snippets), that naturally becomes a common library. I get that it sacrifices some service independence, but is this piece of independence always so worth it, that common libraries are "officially doing it wrong"?
- jedberg 3y agoThe ideal way this works is the Galactus team publishes a library that consumes their API. They build their service, you then use their library to access it. But the key here is that their own library still goes through their API, not directly to their database. So that if you don't want to use their library you don't have to.
- BoppreH 3y agoAh, that's exactly what I've been doing, and unconsciously assumed here. I can attest it works very well, hence my confusion.
- zten 3y agoThe way to do this wrong (by forcing the use of the "blessed" client or common platform dependencies, instead of leaving it open to the consumer as you suggest) is discussed in this talk: https://www.microservices.com/talks/dont-build-a-distributed-monolith/ https://www.microservices.com/talks/dont-build-a-distributed...
- davedx 3y ago> If you are sharing data structures and a common "core" library across services, yes, you are officially doing it wrong. Let me reply in the spirit of your comment: No, you are officially wrong. [0] [0] Says me.
- rco8786 3y ago> Shared data structures? Common library? Services have an API. If you are sharing data structures and a common "core" library across services, yes, you are officially doing it wrong. I agree w/ your other take re: the database but don't understand this one. Sharing data structures between client and server, particularly request/response structures, is hugely useful, certainly not an antipattern. And similarly the idea that if you share a common lib across service boundaries then that's "wrong" falls apart at even the most cursory glance. Service A and B both depend on Service C. So there's a shared client library/SDK that Service A and B use. As just a trivial example.
- preseinger 3y agoif you share data structures in this way you're introducing source-level coupling between services but one of the major reasons to break up logic into separate services is to eliminate source-level coupling a consumer should not need to import service X's SDK in order to use service X, if that is the case then it introduces all kinds of bad situations like "well if i change my service X's behavior in some backwards-incompatible way, it's fine, as long as i update the SDK appropriately" -- wrong etc. etc.
- prpl 3y agoDo you even gRPC/avro/thrift/IDL bro? I know those aren’t the data structures you probably meant, but those are data structures that are commonly shared. That’s different than a model though, which I think is really what OP is talking about. There’s no problem in sharing a library but it is a model which works best with a monolith or a monorepo (and obviously one language). You definitely DO NOT want multiple versions of your library kicking around.
- lelanthran 3y ago> Shared data structures? Common library? Services have an API. If you are sharing data structures and a common "core" library across services, yes, you are officially doing it wrong Quite correct. Each service should have it's own duplicated copy of `someUtilityFunction()`. Then instead of applying 100 fixes to it you can have 100 different behaviours for some pice of business logic. /s
- mjr00 3y agoBusiness logic belongs in services, not libraries. If you're copying nontrivial business logic into different services, that's a big red flag; you need to pick a service to be the single source of truth for that business logic and have other services call into it. edit: Libraries don't solve this, to be clear. Let's say you have 100 services that reference `someUtilityFunction()` in SharedLib==1.0. Oops, there's a bug! OK, let's publish SharedLib==1.1. Great... now you still need to go and update, test, and deploy 100 different services, because they're not going to pick up the corrected business logic otherwise. And of course, you might have the case where someone got lazy and didn't upgrade and they're still using SharedLib==0.0.1-alpha, and there's a bunch of breaking library changes that happened between that version and 1.0 that were never implemented...
- lelanthran 3y ago> Libraries don't solve this, to be clear. Let's say you have 100 services that reference `someUtilityFunction()` in SharedLib==1.0. Oops, there's a bug! OK, let's publish SharedLib==1.1. Great... now you still need to go and update, test, and deploy 100 different services, because they're not going to pick up the corrected business logic otherwise. And of course, you might have the case where someone got lazy and didn't upgrade and they're still using SharedLib==0.0.1-alpha, and there's a bunch of breaking library changes that happened between that version and 1.0 that were never implemented... Yeah, but that relies on a lot of "ifs" when a bugfix is done. If the services don't have tests before deployment, you'll have problems. If a rarely-updated service doesn't get the fix, you have problems. If someone got lazy and didn't upgrade (how is that even possible? they don't have to do anything to get the new thing other than rebuild, redeploy) you'll have problems. OTOH, there's no "ifs" about getting a bugfix when they're all using the same copy of `someFunction()`. When they're all using the same copy of `someFunction()`, the bugfix will go in with the next build of their service, without them even knowing. After all, you don't have each team keep their own copy of the kernel, do you?
- robertlagrant 3y ago> but I'm assuming that wasn't done here I assumed it was done here. Why wouldn't it have been? There's no evidence of that. > Shared data structures? Common library? Services have an API I assumed that was what they meant: common data structures being whatever the various APIs emitted and received. You don't need more than that if you're two people shipping a full product. People aren't buying your internal architecture, and it sounded as though they built it so the code could be separated out in future if necessary.
- preseinger 3y agoif you're two people then there is absolutely no reason to introduce a network API boundary between functional components if you do have an API boundary then it's important to understand that interactions against that boundary are resolved at runtime -- in contrast to data structures which are resolved at compile time if you break something out to a service that's only accessible over a network boundary then there's not much sense in requiring access to go thru some SDK library, right?
- langfan 3y agoOr even through SQL views, if applicable.