Bird
Raised Fist0

Design a Workflow Engine - Step, Transition, Condition, Executor, State Store

LLDhardInterview freq: medium
Choose your preparation mode4 modes available
🏗️
Design a Workflow Engine - Step, Transition, Condition, Executor, State Store
Design a flexible, extensible workflow engine supporting conditional transitions and state persistence.
hardAdvanced LLD ChallengesFreq: medium
Prerequisites:OOP BasicsFinite State MachinesJava Concurrency Basics
⚙️
Functional Requirements
  • Workflow consists of Steps connected by Transitions
  • Transitions have Conditions that determine if they are taken
  • Executor runs business logic when entering a Step
  • State Store persists current Step and data of each workflow instance
  • Support concurrent execution of multiple workflow instances safely
  • Allow dynamic addition of new Condition and Executor types without modifying core engine
  • Provide APIs to start, pause, resume, and query workflow instances
👤
Use Cases
  • Define a workflow with multiple steps and conditional transitions
  • Execute a workflow instance progressing through steps based on conditions
  • Persist and retrieve workflow state for recovery and audit
  • Support concurrent execution of multiple workflow instances
  • Allow custom condition and executor implementations
  • Handle failure and retries in workflow execution
🧑‍💻
Actors
  • Workflow Designer
  • Workflow Executor
  • System Administrator
  • External Event Source
❓
Clarifying Questions to Ask
  • How many concurrent workflow instances are expected to run? (Concurrency level)
  • Are transitions evaluated synchronously or asynchronously?
  • Is there a pricing or cost model for workflow execution steps?
  • What happens if a condition evaluation or executor fails? Retry or abort?
  • Can multiple transitions be taken simultaneously from one step?
  • Is the state store in-memory or persistent DB? Is it shared across threads?
  • Are conditions and executors stateless or do they maintain internal state?
Out of Scope
✗ UI for workflow design
✗ Distributed workflow execution across multiple machines
✗ Complex event processing or time-based triggers
✗ Detailed audit logging and monitoring
⚡
QPS Calculation
write qps1000
read qps5000
rationaleEstimated based on expected concurrent workflow executions and queries.
🖥️
Server Estimate
5
Storage estimate based on average workflow instance size and retention period. Server count based on concurrency and throughput requirements.
🔢
Enumerations
enum StepType
START | INTERMEDIATE | END
Defines the role of a Step in the workflow lifecycle. Using enum prevents typo bugs and enables exhaustive switch statements in Java.
enum TransitionType
CONDITIONAL | UNCONDITIONAL
Specifies if a Transition requires condition evaluation. Enum ensures type safety and clear semantics.
enum WorkflowStatus
RUNNING | PAUSED | COMPLETED | FAILED
Represents the lifecycle state of a workflow instance. Enum usage prevents invalid states and supports state machine logic.
📦
Workflow
3 fields
String idMap<String, Step> stepsList<Transition> transitions
Represents the entire workflow definition including steps and transitions. Manages workflow structure and validation.
public Step getStep(String stepId)Returns the Step object by its identifier.
public synchronized void addStep(Step step)Adds a new Step to the workflow definition atomically.
public synchronized void addTransition(Transition transition)Adds a Transition connecting steps atomically.
📦
Step
3 fields
String idStepType typeExecutor executor
Represents a node in the workflow. Executes business logic when entered.
public void execute(WorkflowInstance instance)Invokes the assigned Executor to perform step-specific logic.
📦
Transition
5 fields
String idStep fromStepStep toStepCondition conditionTransitionType type
Represents a directed edge between two Steps with an optional Condition to control flow.
public boolean isTriggered(WorkflowInstance instance)Evaluates the Condition to determine if this Transition should be taken.
📦
Condition
0 fields
Abstract base for condition logic that decides if a Transition is valid.
public abstract boolean evaluate(WorkflowInstance instance)Evaluates condition against the current workflow instance state.
📦
Executor
0 fields
Abstract base for executing business logic when a Step is entered.
public abstract void execute(WorkflowInstance instance)Performs step-specific actions.
📦
WorkflowInstance
5 fields
String instanceIdWorkflow workflowvolatile Step currentStepWorkflowStatus statusMap<String, Object> data
Represents a running instance of a Workflow, tracks current state and data.
public Step getCurrentStep()Returns the current Step of this workflow instance.
public synchronized void setCurrentStep(Step step)Atomically updates the current Step.
public synchronized void updateStatus(WorkflowStatus newStatus)Atomically updates the workflow instance status.
📦
StateStore
1 fields
Map<String, WorkflowInstance> instanceStore
Persists and retrieves WorkflowInstance states. Supports concurrency-safe access.
public synchronized void saveInstance(WorkflowInstance instance)Persists or updates a workflow instance atomically.
public synchronized WorkflowInstance getInstance(String instanceId)Retrieves a workflow instance by ID.
📦
WorkflowEngine
1 fields
StateStore stateStore
Coordinates workflow execution, manages transitions, and persists state.
public synchronized WorkflowInstance startWorkflow(Workflow workflow)Creates and starts a new workflow instance.
public synchronized void proceed(WorkflowInstance instance)Evaluates transitions and moves the workflow instance forward.
🔗
Relationships
Workflow◆──►Step[1..*]
compositionSteps cannot exist without Workflow
Workflow◆──►Transition[0..*]
compositionTransitions cannot exist without Workflow
🗄️
workflow_instances
PK: instanceId
instanceId STRINGworkflowId STRINGcurrentStep STRINGstatus STRINGdata MAP<STRING, STRING>
Stores the state and data of each workflow instance.
🗄️
workflows
PK: workflowId
workflowId STRINGstepId STRINGstepType STRINGexecutorClass STRING
Stores workflow definitions including steps.
🔌
POST
/workflow/start
Request
{
  "workflowId": "String"
}
Response
{
  "instanceId": "String",
  "status": "String"
}
Starts a new workflow instance for the given workflow.
🔌
POST
/workflow/{instanceId}/proceed
Request
{}
Response
{
  "currentStep": "String",
  "status": "String"
}
Moves the workflow instance forward by evaluating transitions.
🔌
GET
/workflow/{instanceId}/status
Request
{}
Response
{
  "currentStep": "String",
  "status": "String",
  "data": "Map<String, Object>"
}
Retrieves the current status and data of a workflow instance.

Practice

(1/5)
1. In designing the ATM system, should the Card entity be modeled as an abstract class or an interface, considering it has state (card number, expiry) and behavior (validate PIN)? Why?
easy
A. Abstract class, because Card has shared state and behavior that concrete card types inherit.
B. Interface, because Card only defines behavior and no state, allowing multiple inheritance.
C. Interface, because Java interfaces can now have default methods to hold behavior and state.
D. Abstract class, because it enforces implementation of all methods without sharing state.

Solution

  1. Step 1: Analyze Card's characteristics

    Card holds state (card number, expiry) and behavior (validate PIN), so it needs to share state and common logic.
  2. Step 2: Choose between abstract class and interface

    Abstract class supports shared state and behavior inheritance. Interface cannot hold instance state, only constants and default methods without instance fields.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Shared state + behavior -> abstract class [check]
Hint: Stateful base entity -> abstract class, not interface [check]
Common Mistakes:
2. The current Tic-Tac-Toe design uses a WinStrategy interface for win detection. To add a WeekendWinBonusStrategy that applies only on weekends, what is the minimum change required to comply with SOLID principles?
medium
A. Create a new WeekendWinBonusStrategy class implementing WinStrategy and use it without modifying existing classes.
B. Add a boolean flag in the Board class to toggle weekend mode and update all win checks accordingly.
C. Modify the existing WinStrategy implementations to check the day and apply bonus logic.
D. Combine all win detection logic including weekend bonus into a single monolithic class.

Solution

  1. Step 1: Understand OCP and DIP in current design

    Open-Closed Principle (OCP) requires adding new behavior without modifying existing classes. Dependency Inversion Principle (DIP) encourages depending on abstractions.
  2. Step 2: Evaluate options for minimal change

    Create a new WeekendWinBonusStrategy class implementing WinStrategy and use it without modifying existing classes. adds a new strategy class but requires changing client code to use it, violating minimal change. Add a boolean flag in the Board class to toggle weekend mode and update all win checks accordingly. adds a toggle flag in Board, allowing existing strategies to adapt behavior without modifying their code, preserving OCP better than A or D.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Toggle flag in Board -> minimal change, respects OCP [check]
Hint: OCP: add new class or toggle flag, avoid modifying existing [check]
Common Mistakes:
3. The Elevator System needs to support ElectricCar vehicles requiring ChargingSpot types. What is the minimum change to the current Java design to add this without modifying existing classes?
hard
A. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility.COMPATIBILITY_MAP; no existing classes modified.
B. Modify Vehicle base class to add isElectric flag and add charging logic directly in ElevatorController.
C. Create a new ElectricVehicle interface and modify all existing vehicle classes to implement it.
D. Add a new ChargingSpot class and modify Elevator class to handle charging spots explicitly.

Solution

  1. Step 1: Extend enums and create subclass

    Adding new enum constants for vehicle and spot types allows type-safe extension without changing existing classes.
  2. Step 2: Update compatibility map externally

    Updating SpotCompatibility.COMPATIBILITY_MAP to include ElectricCar and ChargingSpot relations extends behavior without modifying existing class code.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Enum extension plus compatibility map update avoids modifying existing classes [check]
Hint: Extend enums and maps, no existing class changes [check]
Common Mistakes:
4. You need to add support for ElectricCar and ChargingSpot in the existing parking system using the Iterator pattern. What is the minimum change to the current design to support this without modifying existing classes?
hard
A. Modify Vehicle and Spot classes to include electric-specific fields and logic, then update iterators accordingly.
B. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map; existing classes remain unchanged.
C. Create a new ElectricVehicle interface extending Vehicle and a ChargingSpot class, then modify existing iterators to handle them.
D. Add a flag isElectric to Vehicle class and modify SpotCompatibility to check this flag during iteration.

Solution

  1. Step 1: Identify extensibility requirements

    Adding ElectricCar and ChargingSpot should not require modifying existing classes to respect OCP.
  2. Step 2: Evaluate options

    Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map; existing classes remain unchanged. adds new enums and classes without modifying existing ones, preserving OCP. Modify Vehicle and Spot classes to include electric-specific fields and logic, then update iterators accordingly. modifies existing classes, violating OCP. Create a new ElectricVehicle interface extending Vehicle and a ChargingSpot class, then modify existing iterators to handle them. adds interfaces but requires modifying iterators, violating OCP. Add a flag isElectric to Vehicle class and modify SpotCompatibility to check this flag during iteration. modifies Vehicle class, violating OCP.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Adding new enums and subclasses without changing existing classes respects OCP [check]
Hint: Add new types via enums and subclasses, no existing class changes [check]
Common Mistakes:
5. ElectricCar requires ChargingSpots in the parking lot system. What is the minimum change to support this without modifying existing classes?
hard
A. Add charging logic inside existing Spot class with conditional checks for electric vehicles.
B. Modify Vehicle class to include a boolean isElectric field and add charging logic inside existing methods.
C. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map.
D. Create a new ChargingSpot class extending Spot and modify ParkingLot to handle it explicitly.

Solution

  1. Step 1: Identify extensibility requirements

    Supporting ElectricCar and ChargingSpots requires new types without changing existing class logic to respect OCP.
  2. Step 2: Apply minimal change approach

    Adding new enum values and subclass ElectricCar allows extension without modifying existing classes. Updating SpotCompatibility map centralizes compatibility logic.
  3. Step 3: Eliminate options violating OCP

    Options B, C, and D modify existing classes or add conditional logic, violating OCP and risking class explosion or god classes.
  4. Final Answer:

    Option C -> Option C
  5. Quick Check:

    New enums + subclass + compatibility map update -> minimal, OCP-compliant [check]
Hint: Add enums + subclass + compatibility map, no existing class changes [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard