Complexity arises in software at the interfaces of systems. Putting a bunch of small apps into a larger container doesn’t address the elephant in the room of how to make these pieces work well with one another.
It doesn’t matter that they aren’t in the same repo, the minute one part of the app expects another to behave in a certain way there is a dependency regardless if that is expressed in code or not.
> It doesn’t matter that they aren’t in the same repo, the minute one part of the app expects another to behave in a certain way there is a dependency regardless if that is expressed in code or not.
There's very little of this in this type of architecture. The dependencies are mostly "how do I deep link from one to the other" (which is generally kept to a minimum if you split the apps correctly), and a bit of "I called an api that stored data that another app will retrieve), but if you were planning on using something like a GraphQL schema on the BE, its not very different from having a public API used by a lot of integrators.
You still reduce build time, the amount of code someone has to wrap their head around to fully understand a given app, how dev tools scale, how deployment works, how long it takes to rewrite a section of the greater system from scratch, how easy it is to upgrade libraries, etc.
That's why you specify those relationships in code. Don't depend on non-public behavior, or have a poorly-defined specification of what the behavior even is.
Micro Frontends alone are not a silver bullet. But they allow teams to operate asynchronously. Whether you're using micro frontends or not, or have one UI monolith, those interfaces can be rigid or poorly defined.
Maximize writing functional, declarative-style UI code. Minimize writing code with side effects (though, for enterprise systems, it is unavoidable: You'll most certainly need things like polyfills to support older browsers, and those polyfills will conditionally augment the global scope).
Every line of code is a liability. Think critically when authoring of how that line could impact other systems and teams that you share the DOM with.
But there can still be a benefit in disentangling a monolith where some of the parts have no interactions with other parts.
I'm not a devotee of microservices, and most of my experience involves front-end web stuff. I've come across quite a few monoliths in that context that were more difficult to work with than they had to be because, for example, various 'services' had their db and view layers tangled up together even though they really didn't have anything to do with each other. A more 'microservice-y' approach would've made things much easier to work with.
I do agree that a bunch of microservices that end up depending on each other doesn't necessarily improve much, though, and often actually causes problems.
I think this is spot on. The problem lies in the incompatible interfaces.
I've been studying category theory recently and it is amazing how well things compose when interfaces follow monoidal / monad design patterns. They are so generalized that they can be used in so many different places. Unfortunately it is so rarely used outside of more academic environments.
If libraries/frameworks were structured to follow these kind of well defined "interfaces" I think we would have a very different experience than the one we have now.
Such monad interfaces are best enforced by language / compiler, however it is non-tractable to do that. Even in Haskell they just move the monoid check to programmer (especially associativity). What is worse, if the law is violated in some tiny subset of the data, that can lead to non-trivial bug. That is why it is difficult to apply in real world, which is usually very complicated :-(
It doesn’t matter that they aren’t in the same repo, the minute one part of the app expects another to behave in a certain way there is a dependency regardless if that is expressed in code or not.