Fathom: So... Microservices
Tags: webdev, devjournal, programming, rust
Published: 2022-05-19 18:31:03


This is the second post in a series of devlogs. I invite you to go take a look at the first post for the project brief if you're confused about why Fathom exists at all.

---

As I've begun this project, the most pressing concern has been exploring the architecture design space, and selecting one with that will inhibit growth the least as Fathom grows. The phrasing here is deliberate, since any architectural decisions made now are invariably made with incomplete information, their founding assumptions made partially or completely incorrect.

The project brief explicitly describes a microservices-based architecture, which presents an interesting challenge. In spite of its many benefits, microservices by their nature have an up-front cost implicit in their adoption; that of the bring-up/strike (to use an AV term) of the services repeatedly during early development. More concretely, since Fathom barely does anything at the moment, it is guaranteed that inter-module interfaces will need to be refactored or rewritten. Until that API can stabilize at least a little, it will be necessary to take down and restart multiple services on every debug-compile-run cycle; a significant drag on productivity and a source of developer friction.

This consideration has _implications_, in that it is currently (in my mind at least) better to continue with a monolithic application until Fathom reaches MVP (at the least) at which point I'll have an existing API to analyze and work from, if only for the reduced development friction. This does not mean, however, that we cannot co-opt some strategies from microservices in order to improve Fathom's architecture. Indeed, the current app/endpoint/service/base split across crate (Rust's unit of compilation) boundaries and the explicit per-crate declaration of dependencies should go a long way to enforcing a clear separation that may be useful when it comes time to split off individual microservices. We can (and indeed must) go a bit further by establishing a few simple rules about which crates can depend on which other crates:

1. Apps (such as the `server` crate) may depend only on endpoints.
2. Endpoints may depend only on the `comm` crate
3. Services may depend only on the `comm` crate.

Aside from architecting the individual components of the back-end, the API that the web interface uses to communicate with Fathom has been on my mind. A number of approaches are available, from REST with JSON, to Websockets, to gRPC. In every case, the question is one of per-message efficiency, especially for binary data. However, they all nevertheless share one problem that I am as yet not sure how to deal with: that of defining the message types across both sides of the application. The ideal situation would be to generate them out of an external spec, but the details of that remain unclear to me. Furthermore, with the current separation between the `web` and `rest` crates, any attempt at defining an API in `rest` would cause `web` to depend upon it, violating rule 2. Adding another shared dependency `ws-comm` that auto-generates stubs for both sides is certainly an option, however.

Either way, the selection of a web<->server communication method is currently blocking all further progress, so that's going to be the focus of the next few days. I'm growing increasingly fond of the `ws-comm` approach, though I am wary of the increased complexity of that that will come with it. Once that's done, I hope to have a basic log-in screen, followed by a browser-only file tree populated by drag & drop implemented over the coming week. We'll see how that goes!

---

If you want to keep track of my moment-to-moment progress, feel free to take a look at the [GitHub repository](https://github.com/Straivers/fathom) for the project.

Also, if you have any questions, comments, or would like to share your own experience with what' I'm talking about, please feel free to leave a comment!