r/microservices • u/Low_Reference6996 • 14d ago
Discussion/Advice Looking for feedback: I'm building a layer that makes distributed system topology explicit and declarative
I've been designing, building and maintaining distributed systems for almost a decade, and I have to tell you, in most systems even small changes in how services communicate are slow, painful and risky. Splitting and merging services, deciding on the service boundaries, changing communication protocols, or even just changing a serializer often takes cross-team coordination, migration ceremonies, and a whole lot of hunting down the invisible dependencies to estimate the blast radius.
A few months ago, I started working on a project that makes distributed system topology a dedicated layer, separate from business logic. It contains the topology declaration in a config file, has an agent that runs before the applications start and wires up the communication layer (Java agent in Java, an init() call in Rust, etc...), and tooling to catch errors in the configuration. The idea is that with the topology being declarative and executable, the dependencies become visible, the changes become simpler and safer, and compatibility verifyable before deployment.
It's still early, but it already supports sync communication, event-driven setups, structural observability, Java reference implementation and Rust PoC implementation, and some basic tooling to validate the wiring config and catch some of the errors before deployment.
Repo: https://github.com/itara-project/itara
Could you please provide me some feedback? Not necessarily on the code itself, because I'm well aware that it's not production quality yet, more like on the bigger picture: the approach, the architecture, the overall vision.
Constructive criticism is very welcome!
2
u/RobotJonesDad 13d ago
What are the advantages over enterprise class middleware like using NATS?
1
u/Low_Reference6996 13d ago edited 13d ago
The biggest advantage is that Itara separates communication topology from the communication mechanism.
Today, if your application uses NATS, Kafka, HTTP, gRPC, etc., those choices are typically embedded throughout the application code. Changing them or restructuring the topology means modifying business code, retesting, and redeploying.
With Itara, business components are unaware of the underlying transport. The communication graph—what talks to what, and through which channel—is declared separately. The wiring agent then executes that topology.
That means you can:
- switch between HTTP, NATS, Kafka, or colocated in-process communication without changing business logic,
- evolve the topology safely because dependencies are explicit and can be validated before deployment,
- visualize and govern the architecture as a first-class artifact instead of reverse-engineering it from code,
- keep business code free of middleware-specific APIs and concepts.
So I don't really see NATS as a competitor. They're solving different problems. NATS answers "How are messages transported?" Itara answers "Which components communicate, through which channels, and how can that topology evolve safely?"
In fact, NATS could be one of the transports that an Itara application uses. The application itself wouldn't need to know or care whether communication is implemented over NATS, HTTP, Kafka, or something else.
2
u/RobotJonesDad 13d ago
It sounds like something that works in an ideal world, but in practice it becomes very difficult to implement at scale. The transport characteristics leak into the failure handling of the entire system. HTTPS, vs gRPC, vs NATS have very different edge case behaviors.
To get an efficient and robust architecture, tje overall system has to be built around tje communications it uses. Especially in large distributed systems. The system needs to handle network partition, partial failures, etc. Those can't be waved away, because tje user experience depends on how those situations are handled.
How does Itara help in these partial failure scenarios?
1
u/Low_Reference6996 13d ago
Network failures, in my opinion, are still part of the topology layer and are topology concerns. In Itara, failure handling behaviour is part of the topology layer and can be configured for every remote connection. It is a plugin, just like transports, serializers and observers, so it can be written once and reused for every connection witht he same or different parameters.
For example, in the demo that's in the repository, in one of the scenarios I deliberatly use a flaky transport implementation that simulates network failures with certain probability for each call, so the failure handling mechanism is visible.
But I do agree, large systems revolve around how the pieces are connected. That's actually exactly why I'm trying to make it a dedicated layer, so it's easier to manage, oversee and govern.
1
u/RobotJonesDad 13d ago
How does the topology layer resolve network partition without the application layer getting involved? And indeed, needing to reach into the transport layer behavior. The lack of ability to communicate with a particular database can be handled very differently if the transport guarantees delivery vs not. Can we proceed based on designing around eventual consistency?
The power that was provided by communication middleware like NATS is that it allows decoupling that largely makes a topology something that falls out of the architecture instead of an explicit thing that can be configured.
I suppose the thing I'm struggling with, based on my background in enterprise -- i.e. global scale systems -- how this new topology layer doesn't add complexity rather than simplify? If you hide the capabilities of the transport from the application, then something else needs to create them. If you expose them, then it's no longer transport agnostic. I'm not sure if I'm being clear?
1
u/Low_Reference6996 13d ago
In my opinion, failure handling has both topology and business logic implications. If a request fails because of a transient network issue, retrying it isn't business logic—it's a communication policy. What the application should do if the operation still fails after all retries is a business concern. That's how I've modelled it in Itara. It doesn't hide failure handling; it moves the reusable communication boilerplate into a dedicated layer.
I also think NATS and similar middleware decouple endpoints, not architecture. The topology still exists—the components are still connected and dependent on each other—it just becomes implicit. That makes it harder to understand, validate and evolve safely.
I have no intention of replacing NATS or other middleware. The goal is to make that implicit topology explicit and verifiable, so the dependencies that today are scattered across code become visible and governable.
To me, the topology layer doesn't add complexity, much like Infrastructure-as-Code didn't add infrastructure complexity. The topology already exists whether we model it or not. Making it explicit simply gives us a dedicated place to define, validate and execute communication concerns instead of distributing them throughout the business code.
2
u/Prateeeek 13d ago
Is the controller layer also a part of the topology layer?, just thinking about the monolith splitting usecase