Well, seems like people love to declare something dead. So far the only thing I truly consider dead is the 3.5' floppy disk.
But there are some things that can be said about your description:
- monoliths are easier to design/test/deploy: not by a longshot. Design isn’t easy because you have to consider all things beforehand otherwise your monolith becomes a patchwork that’s legacy before it hits production. Internal modularity doesn’t provide any sort of guarantees of integrity unless you have a good grasp of all modules and the larger the application, the less chance of that. Testing isn’t easy because … well … you have to test it all (actually, it can be easy, just big in size)
- smaller moving parts leaves less surface of attack: that’s patently false. Fewer moving parts refers to the monolith usually running as one process but even one process can have other requirements (I’m thinking, for example, php — where to run a monolith you have loads of extensions / libraries with their own requirements down to kernel parts). As a whole, microservices may indeed end up having more, but the difference (depending on platform) may not be as significant. However, in the case of a monolith all that surface of attack coming through various software dependencies is concentrated in one place, for microservices it’s distributed.
- scaling : I generally agree with you but I would add that scaling in a logical way for a monolith is impossible at any level. Think an MVC monolith where one application has, say, the UI + API + maybe some sort of asynchronous worker processes. To scale anything, you need to scale them all, introducing perhaps extra access to the API where it wasn’t intended, bringing in potential extra security issues (if not performance).
- microservices needing their own DB: well, that’s textbook microservice architecture, sure. But that only applies if somehow you consider that you need all the benefits of microservices (including them being self contained). However, in practice, what do you get if you have 10 microservices but one database instance? Not a monolith, that’s for sure. But out of 10 potential benefits, you choose to forego 1, still have the rest and you’ve scrapped significant overhead. Depending on your application, that could work for you.
- “splitting the whole system vertically”: that goes only if you’re splitting an existing monolith, sure. But if you’re not splitting and instead you’re designing a new system, the advantage is enormous. Mostly, you can add self contained functionality service by service. Also, you can mix and match technologies (one reason my company went for microservices was that we had a choice between doing a monolith and sticking 10 python devs, vs just designing smaller services and mixing it up between python / java and go depending on developer availability). This is more of a question of mindset, if you design a new application while having a monolith design background, you will think it as a monolith and still having to deconstruct it in your mind first, but if you think it in terms of independent services from the beginning, then it doesn’t matter: you can assemble the services as a monolith if you so choose, but microservices also come naturally in which case orchestration and monitoring are the only particular challenges to overcome.
- the number of moving parts is debatable. You can have 20 microservices on the same machine, so no external network calls, for example. Securing that machine goes the same way as it would for a monolith since you have no extra open ports, for example.
- “Due to the external network calls, the overall latency of the whole application is much higher” — see above, there’s no requirement that the network calls be external, for one. Secondly, think application that needs to integrate with external data sources (external APIs from where you need to fetch data and to which you need to send data). If you go with a by the book monolith, all that work will be done synchronously (high latency). You can try a pseudo-asynchronicity (eg: rather than post to API 1, wait for response, post to API 2, wait for response — you post some data to a queue, a worker within the monolith picks up the work and then calls an internal API that in turn deals with sending / receiving data), but that doesn’t guarantee performance since you are still reusing the same single process / application.