Your word of caution is important. For some projects it's not initially clear what the right data model and bounded contexts are. And for some projects it's just overhead.
But the converse is also true: for some projects/systems it turns out that it being events first is the _only_ way for it to work. I've encountered that the last few years with systems in logistics that show an integral view across parties.
The way we approached that is that we started with a standard system and refactored to event processing approach when the decomposition into bounded contexts was clear.
So in that sense it's similar to the right way of approaching microservices: start out with a 'monolith' that you split up.
When designing C# Anders Heljsberg decided against following Java's checked exceptions. You can find his argument against in the interview from 2003 https://www.artima.com/intv/handcuffs.html
The monadic approach to exceptions like it's done here has the downside of having to encode all your business logic in maps and flatmaps. All the intermediary functions will have to return Try. And all the Try values have to combined into one. That type fidding code feels like cruft that doesn't add much value.
And I say that having developed in Scala for years and years.
Of course there's still sometimes when you want processing of other items to proceed, even though this one has failed. And that's where Try is really essential. So that's where we use it only: at the topmost level.
It's only at that top level that we wrap the call in a Try. And that works rather well.
One reason to do it like this is that we want to leverage the design philosophy of Kotlin. And not be writing Scala or Haskell in Kotlin ). And Kotlins philosophy when it comes to exceptions turns out to be: exceptions. Which is apparent in Kotlin coroutines, channels and flows.
I've developed in Scala for 5 yrs, last yr in Kotlin.
Scala spearheaded from 2007 a lot of features that nowadays are the norm for statically typed programming languages. Odersky is a visionary imo.
For a while I experienced Kotlin as Scala--. (No pattern matching?!? )
A bit later I discerned where Kotlin is treading its own path. The biggest things for me are: non monadic approach to async. Which works really well. And simpler extension functions.
I'm looking forward to trying out union types that are coming to Scala.
I agree. Naming parts of your code is important.
An alternative to named local vals is to either use named functions instead of lambdas: people.filter(olderThan50) or (I use Kotlin) use named extension functions: people.countOlderThan50()
As another Utrechter I'm really impressed with the large bicycle garages on either side of the new railway station. It really makes cycling from and to the station a great experience.
There are quite a few grand new railway stations that have been built in the NL: Rotterdam central station, Utrecht central station, Arnhem station. Recently I was moved when I realised that they're the cathedrals of our time. Shared architectural spaces that are big communal efforts and will be of great value for all for decades to come.
I find checked exceptions interesting. They happen to be the only example of an effect system in a mainstream language. Anders Hejlsberg formulated the main argument against them here [1]
Basically the problem is that checked exceptions are infectious and intermediary code needs to annotate all the exceptions of code that it calls. Which is a chore. Lucas Rytz states in his PhD thesis [2] that the problem is not wich checked exceptions themselves, but that in the java implementation 'not mentioning an exception means if won't happen'. He proposes a system where the default is 'any exception' and being able to turn it off with a compiler flag. And states that the developer experience of that would be much better.
This may become possible in Dotty Scala using the effect system based on implicit functions. But that's in 'proposed' status.
We've been using mobx-state-tree too. What I like is that it's easier to gain an understanding of the moving parts of my application; how actions, derived data and data work together. On the other hand the support for Typescript is a bit clunky and leads to slow compilation. Mitigating the compile time leads to a bit of boiler plate.
The way we approached that is that we started with a standard system and refactored to event processing approach when the decomposition into bounded contexts was clear.
So in that sense it's similar to the right way of approaching microservices: start out with a 'monolith' that you split up.