From Console Application to WJb Action
Most developers do not start with WJb.
They start with a task.
For example:
Send an email.
Before introducing Actions, Jobs, Workflows, or Scheduling, let's solve the problem using plain .NET code.
Step 1. Make It Work
The first goal is not to build a workflow.
The first goal is to solve the problem.
A typical implementation might look like this:
await EmailSdk.SendAsync(
smtpSettings,
new EmailInput
{
To = "john@example.com",
Subject = "Test",
Body = "Hello"
});
At this point there is no WJb.
Just working code.
The most important rule is:
Make the business logic work before introducing infrastructure.
If the email cannot be sent from a simple application, adding WJb will not fix it.
Step 2. Move the Logic into a Reusable Library
Once the solution works, the next step is usually extracting it into reusable code.
public static class EmailSdk
{
public static Task SendAsync(
SmtpSettings settings,
EmailInput input,
CancellationToken ct = default)
{
// send email
return Task.CompletedTask;
}
}
Now the email functionality can be reused from:
- console applications;
- ASP.NET applications;
- worker services;
- unit tests.
The business logic is no longer tied to a specific application.
Step 3. Wrap the Library as an Action
Suppose we now want WJb to execute the same operation.
We do not rewrite the email logic.
We simply wrap it inside an Action.
[ActionName("send-email")]
public sealed class SendEmailAction(SmtpSettings smtp)
: JobAction<EmailInput>
{
public override async ValueTask<ActionResult> ExecuteAsync(
EmailInput? input,
CancellationToken ct = default)
{
await EmailSdk.SendAsync(smtp, input!, ct);
return ActionResult.Empty;
}
}
Notice what changed.
Before:
await EmailSdk.SendAsync(settings, input);
After:
return ActionResult.Empty;
The business logic remains exactly the same.
The Action becomes a WJb entry point for code that already works.
Step 4. Execute the Action Directly
An Action is still just a class.
You can execute it directly without creating Jobs, Workflows, or Schedules.
var action = new SendEmailAction(smtpSettings);
await action.ExecuteAsync(
new EmailInput
{
To = "john@example.com",
Subject = "Test",
Body = "Hello"
});
At this stage:
- no Job is required;
- no Workflow is required;
- no Scheduler is required;
- no Store is required.
You are simply executing an Action.
Step 5. Let WJb Execute It
Once the Action is registered, WJb can execute it on your behalf.
var store = new InMemoryStore();
var wjb = WJbBuilder.Create(store, cfg =>
{
cfg.AddAction<SendEmailAction>("send-email");
cfg.AddService(smtpSettings);
});
await wjb.EnqueueAsync(
"send-email",
new EmailInput
{
To = "john@example.com",
Subject = "Test",
Body = "Hello"
});
await wjb.ExecuteLoopAsync();
The flow becomes:
WJb
↓
Job (Enqueue)
↓
SendEmailAction
↓
EmailSdk.SendAsync()
↓
ActionResult
The email logic has not changed.
Only the execution model has changed.
The same Action can now be:
- executed immediately;
- scheduled for later;
- retried after failure;
- executed on another machine;
- included in a workflow via
.Next(...).
Example of continuing the workflow:
return ActionResult.Empty
.Next("log", $"Email sent to {input?.To}");
The Main Idea
Many frameworks require developers to learn the framework before solving the problem.
WJb takes the opposite approach.
Start with working code:
Problem
↓
Working Code
Extract reusable business logic:
Problem
↓
Working Code
↓
Reusable Library
Wrap it as an Action:
Problem
↓
Working Code
↓
Reusable Library
↓
Action
Then let WJb execute it:
Problem
↓
Working Code
↓
Reusable Library
↓
Action
↓
WJb Execution
Job
↓
Action
↓
ActionResult
↓
Step
↓
Next Job
The business logic remains independent.
WJb does not replace the business logic.
WJb provides a consistent way to execute, schedule, retry, and orchestrate code that already works.
Top comments (0)