Microservices: Artifact = Input + State

Designing microservices is a bit complicated because you have to think about different things when deciding which "domain" it will occupy. A recent discussion with coworkers about our current microservices design led me to rethink how to think about microservices.

This may sound a bit weird for those already working with microservices -- or that managed to build a good view of the building of microservices -- but when the word "artifact" popped up in a discussion, it "clicked" with a bunch of other stuff I had in mind about the topic.

One fact that kept confusing me was that a lot of literature about microservices talk about "domain separation" and how to figure out each domain. Now, although there are a few tricks -- like "if it is a noun, it is a domain" -- not everything is clear cut. Some domains are actually subdomains of a larger domain, so you keep wondering if you should split those or keep them into a single microservice, since splitting them would, invariably, making the microservices coupled (something you want to avoid when building microservices).

And that's where "artifact" felt into place. For a while, I had the impression that microservices had to be built "backwards", in the sense that first you have to think in what you need and then check what you have -- in other words, you have to think on your outputs before checking your inputs. And an "artifact" is, in the end, just the output of the microservice.

In our case, we are dealing with games. Each game has a narration, it has an score, it has statistics and it has a roster. Even if it falls into the "it's a noun!" rule, it actually resembles the required output of our system: we have a request that gets the current narration of a game (which can be polled from time to time or -- as we are currently working on -- pushed towards clients); one request returns the match score (which, again, can be polled or pushed); one request retrieves the match statistics, which is not frequently updated or displayed, so it doesn't need constant updates; and so on. Each one of those is a different microservice, because each one of those is a different artifact.

Just to be clear: our artifacts are kept in a Firestore database, which our clients query directly, most of the time receiving the real time notifications for changes. But another way to keep those artifacts is to have separate services, which the clients would query -- which is really akin to what CQRS describes (well, that is, if your microservice receive commands to change the data, that is; I won't claim them to be CQRS if your microservices are dealing with events directly.)

Alright, if those are artifacts, where does "state" gets into this? The state is the amount of information a microservice has to keep in order to build the artifact. For example, in the narration, each time a new narration comes in, it has to be added to a game list of narrations in order to produce the narration of the whole match. The state can also help this microservice into dropping duplicate narrations.

One "nice" effect of the state is that you can, in theory at least, be able to recognize that even with an input, if there was no change in the state, there should be no change in the artifact and, thus, no output is necessary.

Another thing to keep in mind about the state is that you don't need to keep it in memory; you can use any kind of storage: keep the narrations in a database, keep it on a disk, keep in memory as a cache, or all the above. You pick whatever it is easier to manipulate said state to produce the artifact. The thing to keep in mind is "If this microservice crashes, will it be able to rebuild its previous state when it restarts?"

And, finally, the inputs. Those may sound a bit obvious at first (is your microservice generating data out of thin air?), but keep in mind is that one input can be be the source of more than one microservice. For example, a narration may be consumed by the narration microservice to produce the whole game narration, but it may also be consumed by the score microservice, listening to goal narrations to update its state (if the narration is not a goal, there is no change in score, there is no change in the state and there is no artifact generation).

Returning to artifacts, do not worry if more than one microservice doing almost the same thing, but generating a complete different artifact. As an example, imagine that you want to use push notifications to report new goals. Although this is very close to the score microservice, it produces a different artifact (the push notification vs the update score request) and, by this, should be a complete different microservice. It may even sound wasteful (doing the same thing twice), but it would decouple things if you need some other information in the score (say, adding the name of the players who did the goals) or if you change the artifact consumer (say, you change from an in-hour implementation of pushing to Apple and Google to using a service for this, like Azure).

This change in the my way of thinking about microservices design help me rethink the way we are building our microservices at work, and it is also helping me rethink some things on a personal project (which I hope to finish and show it next year).