serviceA { Promise.All(b,c).then(...) }
serviceB { (d).then(...) }
serviceC { Promise.All(d,e).then(...) }
serviceA { Promise.All(b,c).then(...) }
serviceB { (d).then(...) }
serviceC { Promise.All(d,e).then(...) }
Essentially they construct an AST for whatever services that need to be called. Most of the time they then simply "interpret" the AST with the lower-level Finagle constructs. But, and that's actually the big point, they can use _the same code_ with a different interpreter to generate a GraphViz representation for visual inspection. This super helpful once your call graph becomes more complex .. Another possibility is to write an optimizing interpreter etc.
Anyhow, what I find a bit sad is that they didn't leverage the Scala part if Finagle more .. this would be a prime example for a Free Monad () based DSL. That way they could have avoided writing 10s of classes with less than fortunate names.
() might need a Free Applicative part as well since they inherently want to have parallelism expressed in the language
I can't judge Twitter's design without seeing it, but I don't like the sound of this:
"the simplest web page on twitter.com requires collaboration of dozens of network services speaking many different protocols"
Simplest page? Dozens of collaborating services? Many different protocols?
I'm skeptical this is the most elegant architecture possible that meets the requirements of their simplest web page.
Snappi introduces some mild requirements for how a service exposes it's functions, but once that is done the rest can be automated at deploy time. Not only can service-based load balancers be created dynamically, but RPC stubs can be created for each service and injected into peer services that are dependent on them. For example, if ServiceA needs to access ServiceB, it can do so by referencing this RPC stub knowing that the location of ServiceB will be injected at deploy time to ensure requests are fulfilled for the environment.
There are a number of benefits to this approach for a single application, but one of the larger benefits of consistent specs for contract creation is that individual services will be far easier to share. We're trying to create a structure that will not only empower us to re-use our own services, but also to consume services published by others by simply specifying them as a dependency.
Your work could be perfectly integrated into k8s
- It's hard to figure out what the dependency graph looks like just from promises
- That pattern is verbose and results in gross types when you do it in Java
- With this library, you can easily profile parts of the algorithm to see what's going slow, what's taking a bunch of memory, etc.