# Blueprint Composition Patterns

Blueprints compose multiple packages into deployable infrastructure stacks. This page covers proven patterns for structuring those compositions, managing dependencies between packages, and controlling outputs.

For the basics of creating blueprints, see [Creating Blueprints](https://bluebricks.co/docs/core-concepts/packages/blueprints-overview/creating-blueprints). For dynamic expressions within blueprints, see [Using expr](https://bluebricks.co/docs/core-concepts/packages/blueprints-overview/expr).

## Composition patterns

### Layered architecture

Stack packages in layers: infrastructure, platform, then application. Each layer depends on the one below it.

```yaml
packages:
  - name: terraform_aws_vpc
    id: infrastructure_vpc
    version: 1.0.0

  - name: terraform_aws_subnet
    id: infrastructure_subnet
    version: 1.0.0
    props:
      vpc_id: !expr data.infrastructure_vpc.vpc_id

  - name: helm_nginx
    id: platform_nginx
    version: 1.0.0
    props:
      cluster_name: !expr data.infrastructure_subnet.cluster_name

  - name: helm_myapp
    id: application_myapp
    version: 1.0.0
    props:
      nginx_endpoint: !expr data.platform_nginx.endpoint
```

This gives you clear separation of concerns, predictable dependency flow, and easy maintenance.

### Hub and spoke

A central package provides shared resources to multiple consumers.

```yaml
packages:
  - name: terraform_aws_vpc
    id: hub_vpc
    version: 1.0.0

  - name: terraform_aws_subnet
    id: spoke_web
    version: 1.0.0
    props:
      vpc_id: !expr data.hub_vpc.vpc_id

  - name: terraform_aws_subnet
    id: spoke_db
    version: 1.0.0
    props:
      vpc_id: !expr data.hub_vpc.vpc_id

  - name: terraform_aws_subnet
    id: spoke_cache
    version: 1.0.0
    props:
      vpc_id: !expr data.hub_vpc.vpc_id
```

Spoke packages run in parallel since they share the same dependency. This pattern works well for shared networking, centralized databases, or common security groups.

### Microservices composition

Each service is a separate package with shared infrastructure dependencies.

```yaml
packages:
  - name: terraform_aws_vpc
    id: shared_vpc
    version: 1.0.0

  - name: helm_user_service
    id: user_service
    version: 1.0.0
    props:
      vpc_id: !expr data.shared_vpc.vpc_id

  - name: helm_order_service
    id: order_service
    version: 1.0.0
    props:
      vpc_id: !expr data.shared_vpc.vpc_id
      user_service_url: !expr data.user_service.endpoint

  - name: helm_payment_service
    id: payment_service
    version: 1.0.0
    props:
      vpc_id: !expr data.shared_vpc.vpc_id
      order_service_url: !expr data.order_service.endpoint
```

Services with no cross-dependency run in parallel. Services that consume another service's output run after their dependency completes.

### Environment-specific composition

Use blueprint inputs to configure the same blueprint differently per collection.

```yaml
name: my_blueprint
version: 1.0.0

inputs:
  environment:
    type: string
    description: Environment name
  instance_count:
    type: number
    description: Number of instances

packages:
  - name: terraform_aws_instance
    id: web_instances
    version: 1.0.0
    props:
      count: !expr inputs.instance_count
      environment: !expr inputs.environment
```

Install the same blueprint into different collections with different values:

```bash
# Development collection
bricks install my_blueprint --collection dev --props-file props-dev.yaml

# Production collection
bricks install my_blueprint --collection prod --props-file props-prod.yaml
```

## Dependency management patterns

### Explicit dependencies with version pinning

Pin specific package versions for stability and reproducibility.

```yaml
packages:
  - name: terraform_aws_vpc
    id: vpc
    version: 1.2.0

  - name: terraform_aws_subnet
    id: subnet
    version: 1.1.0
    props:
      vpc_id: !expr data.vpc.vpc_id
```

### Conditional dependencies

Use expressions to control package behavior based on configuration. For a full reference on expression syntax, see [Using expr](https://bluebricks.co/docs/core-concepts/packages/blueprints-overview/expr).

```yaml
name: my_blueprint
version: 1.0.0

inputs:
  enable_monitoring:
    type: bool
    default: true

packages:
  - name: terraform_aws_instance
    id: web_server
    version: 1.0.0

  - name: helm_prometheus
    id: monitoring
    version: 1.0.0
    props:
      target_instance: !expr data.web_server.private_ip
      enabled: !expr inputs.enable_monitoring
```

## Output management patterns

### Aggregated outputs

Combine outputs from multiple packages into a single blueprint output.

```yaml
outputs:
  web_endpoints:
    value: !expr "data.web_server.endpoint + ',' + data.api_server.endpoint"
    description: All web endpoints
  database_info:
    value: !expr data.database.connection_string
    description: Database connection
```

### Conditional outputs

Bluebricks only executes components whose outputs are actually needed. Use conditional outputs to control which packages run.

```yaml
outputs:
  monitoring_url:
    value: !expr "inputs.enable_monitoring ? data.monitoring.url : 'Monitoring disabled'"
    description: Monitoring dashboard URL
```

If `enable_monitoring` is `false`, the monitoring component is not executed. This follows Bluebricks' output-driven execution model: inputs flow down, data flows up based on actual requirements.

### Structured outputs

Organize outputs into logical groups for clarity.

```yaml
outputs:
  infrastructure:
    value: !expr data.vpc.vpc_id
    description: VPC ID
  application:
    value: !expr data.web_app.url
    description: Application URL
  monitoring:
    value: !expr data.monitoring.dashboard_url
    description: Monitoring dashboard
```

## Best practices

* Choose packages that complement each other and verify compatibility
* Minimize dependencies and avoid circular references
* Use clear, descriptive naming conventions for package IDs
* Provide sensible defaults for blueprint inputs
* Test blueprint composition locally before publishing
* Pin package versions for production blueprints

## Anti-patterns to avoid

### Circular dependencies

```yaml
# DON'T: Circular dependency
packages:
  - name: package_a
    version: 1.0.0
    props:
      value: !expr data.package_b.output

  - name: package_b
    version: 1.0.0
    props:
      value: !expr data.package_a.output
```

### Deep dependency chains

```yaml
# DON'T: Long sequential chain prevents parallelism
packages:
  - name: pkg1
    id: pkg1
    version: 1.0.0
  - name: pkg2
    id: pkg2
    version: 1.0.0
    props:
      dep: !expr data.pkg1.out
  - name: pkg3
    id: pkg3
    version: 1.0.0
    props:
      dep: !expr data.pkg2.out
  - name: pkg4
    id: pkg4
    version: 1.0.0
    props:
      dep: !expr data.pkg3.out
  - name: pkg5
    id: pkg5
    version: 1.0.0
    props:
      dep: !expr data.pkg4.out
```

Prefer hub-and-spoke or layered patterns to maximize parallel execution.

### Unclear output references

```yaml
# DON'T: Ambiguous reference
outputs:
  result:
    value: !expr data.pkg.out
    description: Result
```

Use descriptive output names that indicate what the value represents.

## See also

* [Creating Blueprints](https://bluebricks.co/docs/core-concepts/packages/blueprints-overview/creating-blueprints): step-by-step guide for the UI wizard and CLI
* [Using expr](https://bluebricks.co/docs/core-concepts/packages/blueprints-overview/expr): expression language for dynamic configuration
* [Parallel Execution](https://bluebricks.co/docs/core-concepts/runs/parallel-execution): how Bluebricks resolves the DAG and runs packages in parallel
