Decoupled invocation
Callers submit intent without executing the work inline.
The Active Object pattern separates method invocation from method execution. Callers submit work, and an owned worker processes that work asynchronously, often through a queue. This helps when the design would otherwise expose shared mutable state to many threads or when callers should not block on execution directly.
Consider shipment label generation or fraud scoring in an order system. Many request threads may want the capability, but the internal state or external dependency path may be easier to manage through one controlled execution stream than through ad hoc synchronization everywhere.
Active Object combines a proxy-like submission interface, a request queue, and a worker or scheduler that owns the actual execution context. This turns direct method invocation into command submission. The gain is simplified concurrency around the owned object. The cost is asynchronous behavior, queue management, and the need to reason about eventual completion rather than immediate return.
This pattern is often a better choice than wide synchronized access when one component should have clear serialized ownership of behavior.
Callers submit intent without executing the work inline.
One execution context handles mutable behavior predictably.
Backlog, priority, and failure policy become explicit architectural concerns.
| Good fit | Why |
|---|---|
| One component owns mutable workflow state | Serialized execution reduces coordination complexity |
| Callers can continue without immediate result | Asynchronous completion is acceptable |
| Work should be rate-limited or prioritized | The queue becomes a visible control point |
| External calls must be isolated behind a worker | Ownership and retry policy stay centralized |
package org.javaomnibus.fulfillment;
public final class ShipmentPlannerActiveObject {
private final BlockingQueue<ShipmentPlanningTask> queue = new LinkedBlockingQueue<>();
public void submit(ShipmentPlanningTask task) {
queue.add(task);
}
public void runLoop() throws InterruptedException {
while (!Thread.currentThread().isInterrupted()) {
ShipmentPlanningTask task = queue.take();
task.execute();
}
}
}