Skip to main content

Combining Components with Pythonic definitions

As your Dagster project grows, you may want to leverage Components for standardized data pipelines while still maintaining traditional @dg.asset definitions for custom logic. This guide shows how to combine both approaches in a single project using Definitions.merge.

When to use this pattern​

This pattern is useful when you:

  • Want to use Components for standardized integrations (Sling, dbt, etc.), but need custom Python logic for specific transformations
  • Are migrating an existing project to Components incrementally
  • Have team members who prefer working with Python code while others benefit from Components' declarative approach

Example project structure​

Here's a project that combines Components with traditional Pythonic assets:

tree my_project/src/my_project
my_project/src/my_project
├── __init__.py
├── definitions.py
├── assets
│ ├── __init__.py
│ └── analytics.py
├── resources
│ ├── __init__.py
│ └── warehouse.py
└── defs
├── raw_data_sync
│ └── defs.yaml
└── analytics_dbt
└── defs.yaml

5 directories, 8 files

This structure includes:

DirectoryPurpose
defs/Contains Dagster Component definitions
assets/Traditional @asset definitions written in Python
resources/Shared resources used by both Component definitions and Pythonic asset definitions

Combining Component and Pythonic asset definitions​

The key to combining Component with Pythonic asset definitions is using Definitions.merge. This merges multiple Definitions objects together:

my_project/src/my_project/definitions.py
@dg.definitions
def defs():
"""Combine Components and Pythonic assets."""
# Load component definitions from the defs/ folder
component_defs = dg.load_from_defs_folder(path_within_project=Path(__file__).parent)

# Create definitions for Pythonic assets
pythonic_defs = dg.Definitions(
assets=[customer_segmentation, revenue_forecast],
)

# Merge component definitions with pythonic definitions
return dg.Definitions.merge(component_defs, pythonic_defs)

This pattern:

  1. Uses load_from_defs_folder to automatically discover and load Component definitions from the defs/ folder
  2. Creates a separate Definitions object for your Pythonic assets
  3. Merges the Component and Pythonic asset definitions together using Definitions.merge

Sharing resources across both types of definitions​

When Component and Pythonic asset definitions need to share resources (like a database connection), you can bind resources when creating your definitions.

There are two recommended patterns for organizing resources:

Keep resources in a resources/ Python module and bind them in definitions.py. This pattern is best for:

  • Complex resource logic that benefits from being in a dedicated module
  • Resources that need unit testing
  • Sharing resources across multiple code locations
my_project/src/my_project/resources/__init__.py
class MyResource(dg.ConfigurableResource): ...


def get_warehouse_resource():
return MyResource()

my_project/src/my_project/definitions.py
@dg.definitions
def defs():
"""Combine Components and Pythonic assets with shared resources."""
# Load component definitions from the defs/ folder
component_defs = dg.load_from_defs_folder(path_within_project=Path(__file__).parent)

# Define resources available to ALL assets (components and pythonic)
resources = {
"warehouse": get_warehouse_resource(),
}

# Create definitions for Pythonic assets
pythonic_defs = dg.Definitions(
assets=[customer_segmentation, revenue_forecast],
resources=resources,
)

# Merge component definitions with pythonic definitions
return dg.Definitions.merge(component_defs, pythonic_defs)

Your traditional @asset functions can request resources by parameter name:

my_project/src/my_project/assets/analytics.py
@dg.asset
def customer_segmentation(warehouse: MyResource) -> None: ...


@dg.asset(deps=[customer_segmentation])
def revenue_forecast(warehouse: MyResource) -> None: ...

The resource key (warehouse) in the resources dict matches the parameter name in the asset functions, allowing Dagster to inject the resource automatically.

Where resources should NOT live​

Avoid these patterns when organizing resources:

❌ Anti-patternWhy it's problematic
defs/snowflake/defs.yamlResources are not Components. Components produce assets; resources are dependencies. YAML component definitions require a Component class.
resource/snowflake/ (singular)Inconsistent naming and unnecessary nesting. Use resources/ (plural) as a flat module.

Best practices​

When combining Components with Pythonic definitions:

  • Use Components for standardized integrations: Sling for data replication, dbt for transformations, etc.
  • Use Pythonic assets for custom logic: Complex transformations, ML models, or business logic that doesn't fit a Component
  • Share resources through definitions.py: Bind resources at the top level so they're available to all assets
  • Keep resource logic in a resources/ module: Makes resources testable and reusable

Next steps​