Use when a project should use the domino Python library as a lightweight Hydra-configured serial workflow orchestrator, including creating workflow YAML configs, writing Python step functions, wiring ctx data flow, loading callables with module:target specs, and running workflows with domino or python -m domino.
Resources
8Install
npx skillscat add junxiao-zhao/domino Install via the SkillsCat registry.
Domino Orchestrator
Use domino when a project needs a simple serial workflow runner configured by
Hydra-compatible YAML. Prefer it for ordered data jobs, scripts, ETL-like
pipelines, research workflows, or project automation where a shared context
dictionary is enough.
Do not use domino for DAG scheduling, parallel execution, retries, async
execution, persistent state, or distributed orchestration unless those features
are added explicitly.
Design Philosophy
Domino is a lightweight serial workflow orchestrator. Its job is to make workflow
structure explicit, readable, and configurable. A good Domino workflow shows the
ordered operational steps in YAML, while Python provides reusable unit behavior.
Core principle:
Workflow structure belongs in config. Reusable unit behavior belongs in Python.
Prefer Direct Callables
Use the real callable whenever Domino can load it.
Good:
callable: "package.module:function"
callable: "package.client:api.method"Avoid wrapping a callable only to rename it, forward kwargs, adapt a dotted
method, or preserve an old import path.
A wrapper is justified only when it adds real behavior: validation,
transformation, error handling, persistence, or a reusable policy that cannot be
expressed cleanly through kwargs.
Keep Step Functions Atomic
A Domino step should represent one conceptual operation at the workflow level.
Good examples:
- Fetch source data.
- Build an output path.
- Write raw data.
- Merge records.
- Validate a gap.
- Write a manifest.
- Summarize a result.
Avoid a single Python function that internally performs a whole workflow such as
fetch -> transform -> merge -> validate -> persist.
Use Kwargs For Variation
If two workflow steps differ only by parameters, keep the same callable and pass
different kwargs.
Common examples include date ranges, entity ids, field lists, provider options,
calendar policies, output labels, and storage partitions.
Do not create separate wrapper functions or config files for differences that
are only arguments.
Prefer One Config For One Workflow Shape
If two runnable jobs have the same ordered steps and differ only by runtime
values, they should usually share one config.
Use config overrides for runtime differences. Split configs only when workflow
shape, ownership boundary, or operational intent is genuinely different.
Make Component Workflows Self-Contained
A runnable config should contain the complete ctx and complete ordered workflow
needed to run that component.
Avoid generic entrypoints that depend on many overrides or hidden composition
layers when the project has clear components. Generic entrypoints are acceptable
for small one-off projects, but they should not become an indirection layer over
real workflows.
Keep Policies Reusable, Not Source-Specific
When a workflow needs a reusable policy, model the policy directly.
Good:
callable: "quality:warn_if_gap"
kwargs:
expected_dates: "calendar_provider:expected_dates"Avoid separate policy functions for each data source unless the behavior is
genuinely different. Prefer passing policy inputs or provider callables through
kwargs.
Be Explicit With Third-Party APIs
Some third-party callables expose generic signatures such as **kwargs, so
Domino cannot infer parameters from ctx.
In that case, pass required arguments explicitly in YAML. Do not wrap the
third-party API only to create a nicer signature.
Avoid Compatibility Wrappers By Default
Compatibility wrappers preserve old boundaries and make future refactors harder.
Use them only when maintaining a stable public API is more important than
clarity. In internal or early-stage projects, update configs and imports
directly.
Test Architecture, Not Only Behavior
If a project has repeatedly drifted toward hidden workflows or wrapper layers,
add regression tests that scan active configs and source files for forbidden
patterns.
Useful architecture tests may guard against:
- Generic workflow entrypoints returning after removal.
- Local callable loaders duplicating Domino.
- Wrapper callables replacing direct callables.
- Split configs that differ only by kwargs.
- Monolithic sync or run functions hiding workflows.
Decision Checklist
Before adding a Domino step, function, or config, ask:
- Can Domino call the real function directly?
- Is this function hiding multiple workflow steps?
- Is the difference only kwargs?
- Does this config have the same workflow shape as an existing config?
- Is this wrapper adding behavior, or only forwarding?
- Would a future reader understand the workflow from YAML alone?
- Should a regression test prevent this pattern from returning?
If the answers point to direct callable + kwargs + explicit YAML, prefer that
path.
Setup In A Project
Install domino in the target project environment:
python -m pip install git+https://github.com/Junxiao-Zhao/DominoIf using a local checkout of this repository:
python -m pip install -e /path/to/dominoUse this project layout unless the target project already has conventions:
conf/workflow/
my_workflow.yaml
workflow_steps/
steps.pyRun workflows from the target project root:
domino --config-path conf/workflow --config-name my_workflow
python -m domino --config-path conf/workflow --config-name my_workflowHydra overrides can follow the known flags:
domino --config-path conf/workflow --config-name my_workflow ++ctx.trade_date=20260524Config Pattern
Create a YAML config with ctx and ordered workflow sections:
work_dir: /tmp/project-work
ctx:
work_dir: ${work_dir}
trade_date: 20260524
workflow:
load_data:
callable: "workflow_steps/steps.py:load_data"
kwargs: {}
return_key: data
transform:
callable: "workflow_steps/steps.py:transform"
kwargs:
mode: fast
return_key: transformed
save:
callable: "workflow_steps/steps.py:save"
kwargs: {}
return_key: nullWorkflow steps execute in YAML declaration order.
Each step supports:
callable: requiredmodule:targetstring;targetmay be a dotted attribute path.kwargs: optional explicit keyword arguments.return_key: optional key, list of keys, or null.
The module part can be an importable Python module or a .py file path.
Relative file paths are resolved from the current working directory. Thetarget part can be a single callable attribute or a dotted attribute path.
Step Function Pattern
Define step functions as plain Python functions. Domino builds call arguments
from explicit step kwargs first, then fills missing signature parameters fromctx.
Recommended pattern:
def load_data(work_dir, trade_date, **kwargs):
return {"work_dir": work_dir, "trade_date": trade_date}
def transform(data, mode="default", **kwargs):
return {"data": data, "mode": mode}
def save(transformed, ctx, **kwargs):
print(ctx["work_dir"], transformed)Always consider adding **kwargs to workflow step functions. Domino passes all
explicit step kwargs through unchanged, including keys not declared in the
function signature. A **kwargs fallback prevents unrelated config keys from
raising TypeError when configs are shared or evolve.
Argument resolution:
- Start with all explicit step
kwargs. - If a declared parameter is missing and named
ctx, pass the whole context. - If a declared parameter is missing and exists in
ctx, usectx[name]. - Otherwise omit the parameter from the call.
Use Python default parameters for optional inputs.
Return Values
Domino writes non-None return values back into ctx.
return_key: null: store the result under the workflow step name.return_key: some_key: store the result underctx["some_key"].return_key: [a, b]: require the result to be a sequence of the same length,
then store each item under the matching key.- Return
Noneto store nothing.
Example:
workflow:
split:
callable: "workflow_steps/steps.py:split"
kwargs: {}
return_key: [left, right]def split(value, **kwargs):
return [value[:1], value[1:]]Verification
Before handing off a target project workflow, run:
domino --config-path conf/workflow --config-name my_workflowIf the target project uses tests, add at least one smoke test that runs the
workflow or calls the step functions with representative ctx values.
Common failures:
DominoLoadError: thecallablemodule path, module name, target path is
wrong, or the target is not callable.DominoConfigError: workflow config shape orreturn_keyhandling is invalid.TypeError: a step function received explicitkwargsit does not accept; add**kwargsor remove the extra config key.