Event Interception
martinfowler.com
martinfowler.com
The very idea that services should talk directly to another is flawed even on happy days without any migrations and such. There’s service discovery, addressing, load balancing, firewall configurations, rate limiting that really should all be part of one logical connectivity plane. In NATS, your services can run behind a residential NAT if you wanted to, all you need is to connect to the cluster and choose a subject space.
Reverse proxies is probably the most common modern solution though in things like k8s. I’m not wise enough to say if they are better from an architectural pov.
Nats is a at its core a pub-sub system. Everyone (clients and services) connect to the Nats server (or cluster but same principle). Then you can either publish or subscribe to messages, whose payload is typically simple json.
Anyway, the point here is that with such a setup, you don't need to worry about networking between services. How do I stand up new service, let’s call it Galactus? You connect to the nats server, then you listen for messages on a subject space like “galactus.*”. Now anyone else can talk to Galactus, without proxies, api gateways - Galactus doesn’t even need a URL, only a subject (this is what subject based addressing means). As a result, you only worry about networking once.
These systems have been around and greatly improved for all kinds of use cases, yet it seems like nobody is paying attention.
The issue is that moving from synchronous API calls to asynchronous message passing introduces new complexity. It's not suitable for every problem. Most places I worked use a combination of synchronous APIs and asynchronous messaging to share information between services, depending on requirements.
Of course in some cases you can just share access to the same database or filesystem too.
This has been a problem I've been trying to solve for years! Surprisingly, it's a fun problem space, mostly because I disagree that it isn't suitable for every problem.
The solution I've been converging on is quite interesting, essentially a non-linear programming language that you write linearly (similar to how CPU branch prediction runs code that hasn't executed yet ... just on a more abstract level). It's pretty neat in how it works under the hood, but on the programmer level, it feels like just another programming language; but like erlang/elixir, the execution can be spread all over the world, or just your machine.
Probably one of the coolest things was when I got optimistic mirroring working, where local code will "predict" what distant code will do and then allow your local code to run to completion but not commit the result until the remote code agrees with the prediction -- and if it disagrees the code has to rerun that portion.
Fun stuff, but still probably a few years away from a HN post about it... it's just a fun side-research project atm.
> Fun stuff, but still probably a few years away from a HN post about it...
Remember that partial results are very useful too. Personally I like reading posts that have unfinished ideas, with commentary. Eg “I needed something to achieve X and I settled on Y. That causes some annoying side effect Z, so I’ll keep looking for a better solution”. That stuff is even more engaging than “look at this almighty finished and perfect system”.
Well yes but request-response is simply a pattern that can be implemented on top of pub-sub extremely easily. Nats implements it with a temporary generated inbox subject for responses, so dead simple. This keeps application logic conceptually identical to traditional rpcs.
The gain though doesn’t come from the messaging patterns themselves though, but the addressing. Going from “I wanna talk to X” to “I wanna talk about Y” is an extremely powerful shift of mindset.
Unfortunately deploying multicast in practice requires having several infrastructure ducks in a row that remain hard to achieve in enterprise environments let alone public clouds, so the devops mafia generally disregard it and deploy sidecar proxies in a unicast mesh instead, with log-structured event stores as the pub/sub core. There has been some continuing research into multicast as middleware³ but to my chagrin (albeit not my surprise) the barriers of infrastructure mean it hasn't developed as an application layer nearly as much as the unicast approaches.
[1] http://www.spread.org but sadly now either defunct or classified, not sure which, the whole project kinda went suddenly silent ca.2018
[2] not zero-copy; you'd have to roll your own for that
[3] e.g. out of the CCRG at UCSC https://ccrg.ucsc.edu/about-our-work/research-areas/routing-...
Neither are particularly straightforward to reason about, but ordering guarantees (in the Lamport sense) are (in my experience) harder to ensure at regional scale with proxies and service meshes. There’s a counterexample, of course: at intercontinental and interplanetary scale we lack the ability to stabilise tree topologies with predictable forwarding latency and are forced to use service proxies and bounded timestamps on replicated data to achieve ordering guarantees instead, of which the best known example is possibly TrueTime. This trades interactivity for scale.
Multicast is definitely not easier to program for, and although I could attribute that to a shortage of high-level libraries, in practice the shortfall is mediated by the infrastructure dependency. This places it beyond the radar of most system designers in commercial environments, thus completing the elements of a downward popularity spiral. It’s a shame, but that’s tech.
Something like that seems like the way forward but I haven’t seen an implementation yet that truly frees devs from worrying about DNS names and IP. K8s can paper over them but they’re still under there waiting to bite you.
But I would argue that Spring's greatest achievement is the Aspect-Oriented interception and how it enabled rapid rollout of caching, reporting, and other event interception deep in the code.
Spring gives you almost for free a very deep and useful event interception model without writing craploads of boilerplate.
How is this different from Anti-corruption Layer pattern https://learn.microsoft.com/en-us/azure/architecture/pattern...
>Implement a façade or adapter layer between different subsystems that don't share the same semantics. This layer translates requests that one subsystem makes to the other subsystem.
It feels like those are very very close or almost the same concepts.
Or that can achieve the same results.