When a product grows, the problem stops being a feature and becomes how the services talk to each other. That is where service boundaries, event delivery and flows that cannot be lost along the way come in.

The symptom is usually the same: two teams can no longer deploy without coordinating, a multi-step process stalls halfway and nobody knows where, or the same piece of data tells different stories depending on which service you ask.
Splitting a monolith is not the default answer. First comes finding where the business boundaries actually are, and only then does anything get separated, with the events and contracts that requires.
A badly cut microservice is worse than the monolith it came from: same coupling, more latency, more deployments. Before anything is split, the map says which part of the business writes which data, and the cut follows that, not the technical layers.
An event is a contract with the future: someone will read it a year from now, and last week’s may need reprocessing. That is why they ship with a versioned schema, a deliberate partition key and idempotent consumers.
An onboarding, a charge or a reconciliation are several steps with waits and failures in between. With Temporal the process state lives outside the service, so a deploy or a crash does not leave anyone stranded, and you can see which step each case is on.
Isolating clients is not just filtering by tenant_id. It is deciding what is shared and what is not across data, configuration, usage limits and deployment, and putting that somewhere it cannot be forgotten when the next endpoint is written.
Order matters here: if the event is published before the write is committed, sooner or later you publish something that never happened.
Service A
Outbox
Kafka
Service B
Writes the change and the event in the same transaction
A publisher reads the table and publishes, at least once
Consumes its partition, in order per key
If it already processed that id, it drops it
Commits the offset only once it is done
Reserve, charge and confirm are three steps with three different owners. Temporal keeps track of where it stopped, so a restart does not erase the progress.
Scheduled
Something triggers it and it is recorded with its business id.
Running
Each step is an activity with its own timeout.
Waiting to retry
The provider answered badly or not at all. It waits and tries again.
Completed
The result is written once, even if the step ran more than once.
Compensated
If there is no way through, the work is undone and the case is closed rather than left hanging.
Retries are part of the design, not an exception: backoff, a cap on attempts and, at the end, an explicit compensation.
Almost never entirely, and never as the first move. A badly cut microservice is worse than the monolith it came from. The first step is seeing which part of the business writes which data; sometimes the answer is extracting two services and leaving the rest alone.
By migrating one full end-to-end flow before touching anything else. The risk shows up small, the team learns the pattern on a real case, and the product keeps delivering meanwhile.
It depends on whether the past needs rereading. A queue hands out work and forgets it; Kafka keeps the log, so a new consumer can reprocess last week. If nobody is going to reprocess anything, a plain queue is cheaper to run.
When the process has several steps with waits and different owners: an onboarding, a charge, a reconciliation. With Temporal the state lives outside the service, so a deploy or a crash leaves no case half done, and you can see which step each one is on.