For over a decade, microservices have been the poster child for modern software architecture. They promised to liberate us from the tangled web of monolithic applications, offering scalability, independent deployment, and team autonomy. And in many ways, they delivered. But this revolution came with its own baggage—a hidden operational cost often called the "microservice tax."
What if we could keep the benefits of decoupled services without the crushing weight of their complexity? What if we could break down business logic even further, to its most fundamental, indivisible unit?
This is where action.do enters the picture. It’s not about replacing microservices entirely but about introducing a new, more granular primitive: the atomic action. This shift in perspective is the key to simplifying service-oriented architecture and building truly robust agentic workflows.
A microservice is an independently deployable service built around a specific business capability. For example, you might have a UserService, an OrderService, and an InventoryService.
While powerful, this approach carries significant overhead:
action.do proposes a simpler, more foundational building block. Instead of a "service" that handles a broad business capability, action.do focuses on a single, atomic action.
An atomic action is a single, indivisible operation that either completes successfully or fails entirely, leaving no partial state.
Think of it this way:
These actions are the verbs of your business. They are single-purpose, focused, and designed to be the fundamental Lego bricks for your Business-as-Code.
| Feature | Microservices | action.do Atomic Actions |
|---|---|---|
| Granularity | Broad business capability (e.g., User Management). | A single, indivisible task (e.g., send-welcome-email). |
| Scope | Manages its own data, logic, and state. A "mini-application." | Executes one operation. It's stateless and focused. |
| Overhead | High. Requires dedicated infrastructure, deployment pipelines, and network management. | Low. Invoked via a simple API call. The platform handles execution and scaling. |
| Composition | Complex choreography through events or direct API calls. Hard to trace. | Explicit orchestration within a workflow.do. Easily auditable and visible. |
| Idempotency | Not guaranteed. Often requires complex application logic to prevent side effects. | A core principle. Executing an action multiple times has the same effect as executing it once. |
Let's see how this plays out in a common scenario: onboarding a new user.
The Microservice Way: You might have a UserOnboardingService. When a user signs up, you send a request to this service. Internally, this service might call the UserService to create a record, the EmailService to send a welcome message, and the AnalyticsService to track the event. If the EmailService fails, the UserOnboardingService is responsible for rolling back the changes or retrying, adding significant complexity to its internal logic.
The action.do Way: You define a workflow.do composed of several atomic actions:
Each step is a distinct, auditable action.do call. The workflow orchestrator handles the sequencing, retries, and error handling. The logic is explicit, not hidden inside a service.
Executing an action is as simple as this:
import { Do } from '@do-sdk/core';
// Initialize the .do client with your API key
const a = new Do(process.env.DO_API_KEY);
// Execute a specific, atomic action with parameters
const { result, error } = await a.action.execute({
name: 'send-welcome-email',
params: {
userId: 'usr_12345',
template: 'new-user-welcome-v2'
}
});
if (error) {
console.error('Action failed:', error);
} else {
console.log('Action Succeeded:', result);
}
Because send-welcome-email is designed to be idempotent, the workflow can safely retry it upon failure without the risk of sending duplicate emails. This makes your system inherently more resilient.
The beauty of action.do is that it doesn't have to be a rip-and-replace for your existing microservices. In fact, it's the perfect simplification layer.
Do you have a legacy InvoiceService with ten different REST endpoints? You can wrap each critical operation as a distinct atomic action.
By doing this, you instantly gain the benefits of the .do platform:
Microservices were a necessary reaction to the monolith, breaking down large problems into smaller ones. Atomic actions are the next logical evolution, breaking down services into their core, executable components.
By focusing on single-purpose, idempotent, and auditable actions, action.do strips away the incidental complexity of service-oriented architecture. It allows you to focus on the essential logic of your business, providing the perfect primitives for powerful agentic workflows and reliable automation.
It’s time to move beyond the service and think in actions. Execute. Audit. Repeat.
Q: What constitutes an 'atomic action'?
A: An atomic action is a single, indivisible operation that either completes successfully or fails entirely, leaving no partial state. Examples include sending a single email, making one API call, or writing a single record to a database.
Q: Why is idempotency important for actions?
A: Idempotency ensures that executing the same action multiple times with the same parameters has the same effect as executing it once. This is crucial for building reliable systems that can recover from failures without causing unintended side effects, like sending duplicate invoices.
Q: How does action.do relate to a workflow.do?
A: action.do represents the individual steps or building blocks. A workflow.do is a sequence or graph of these actions orchestrated to achieve a larger business outcome. You compose workflows from one or more atomic actions.
Q: Can I define my own custom actions?
A: Yes. The .do platform allows you to define your own custom actions as functions or microservices, which can then be invoked via the action.do API. This turns your existing business logic into reusable, auditable components.