I agree that microservices need to be considered seriously with real engineering thought and not jumped onto to pad CVs. They are a tool just like anything else, and need to be used carefully. Microservices have a particularly sharp edge.
However libraries feel like a very simplistic argument against microservices. In a world where you have a single monolith, then libraries may work fine, but often you have 2, 3 maybe 4 decently sized monoliths. In that situation libraries may not make much sense when you need to reference a central source of data.
It depends on how many services connect directly to the database. More than one and there be dragons.
The biggest problem is schema changes, because they require the library to be changed. The apps now need to match their library versions against a schema they don’t control. It’s messy to schedule and rollbacks suck really bad.
A microservice that returns data from the database can paper over a lot of schema changes, because the responses aren’t directly tied to the schema. Data can be shared without clients ever knowing, entities can be moved and split between tables without clients knowing, etc.
I try hard to only have 1 service connect to a database directly. If there’s only 1 app, it can have a direct connection. If there’s more than one, I like to chuck a micro service in front of it.
> The biggest problem is schema changes, because they require the library to be changed.
This is only true if multiple applications are accessing the same tables, and even then, only if the changes aren’t backwards-compatible. For example, adding a column shouldn’t be a breaking change, because no one should be doing SELECT * (and also then assuming a fixed width when parsing).
I think another point of common confusion is how overloaded the word “schema” is. Database schema, i.e. how tables are logically related? Table schema? A synonym for database, as MySQL has it? A logical grouping of tables, procedures, etc. within a database, as Postgres has it?
It’s entirely possible (and generally much cheaper, and with less maintenance overhead) for multiple services to share a database cluster/server/node, but have complete separation from one another, modulo indirect impact via the buffer pool.
Databases in practice are surprisingly dangerous with multiple direct services, even with just readers. Your schema effectively becomes your protocol.
Need to add a column or perform delicate schema alterations? You run the risk of breaking dependents.
Have parts of your database that are sensitive (i.e. PII)? Your DB platform will need to reflect that to avoid arbitrary queries.
Is it possible to perform a query that recursively joins dozens of tables together on every entry? Your control is limited in how you could prevent them.
Who wrote that breaking change? Or, did someone's service get compromised? Hope you aren't sharing credentials for access among services.
The same caveats for managing backwards compatibility of HTTP routes/endpoints apply to managing database tables/columns.
> Need to add a column or perform delicate schema alterations? You run the risk of breaking dependents.
Create a stable-interface view over your unstable-interface underlying table, and query that view. Or version your tables (my_table_v1 -> my_table_v2) and create a new table for each breaking change.
> Have parts of your database that are sensitive (i.e. PII)? Your DB platform will need to reflect that to avoid arbitrary queries.
Use views to exclude PII fields or aggregate/hash them.
> Is it possible to perform a query that recursively joins dozens of tables together on every entry? Your control is limited in how you could prevent them.
Databases are designed for multitenancy, they have mechanisms to cap the resource usage of a query.
> Who wrote that breaking change? Or, did someone's service get compromised? Hope you aren't sharing credentials for access among services.
Not sure about MySQL, but Postgres has the pgaudit extension which handles provides this visibility for DML/DDL queries out of the box, as long as you create a DB user for each use case.
Then you'd have multiple monoliths share the same database. For me that's where I've zero experience, because all engineers more senior to me have cordoned that off and have said that's a no go area.
But in all seriousness, that would make data migrations really tricky, as well as handling differing versioning etc. NVM extending that to more apps, feels like a dangerous venture
The alternative would be "two applications talking to the same microservice" where you run into the same issues with backwards compatibility, except the API is now "application to microservice" instead of "application to database schema". Either way, when someone changes the interface they either need to do this in a backwards compatible fashion or all dependent applications need to be updates.
I think the usual approach is to have each microservice store its own local version of the data - but you can do that with a database, just use a different table. the value is in scaling - if one service needs a big database but another needs fast access, etc. overall, nobody other than the top 50 (let's be generous) tech companies needs this.
However libraries feel like a very simplistic argument against microservices. In a world where you have a single monolith, then libraries may work fine, but often you have 2, 3 maybe 4 decently sized monoliths. In that situation libraries may not make much sense when you need to reference a central source of data.