# Layered Stack Configuration

_Atmos Design Pattern_

The **Layered Stack Configuration** pattern organizes components into infrastructure layers based on their function—similar to how network architectures have distinct layers (network, transport, application). Each layer groups related components and can be managed independently.

## Use-cases

Use the **Layered Stack Configuration** pattern when:

- You have many components that naturally group by function (networking, data, compute, etc.)

- Different teams own different layers of infrastructure

- You want to import only the layers needed for a specific environment

## Benefits

The **Layered Stack Configuration** pattern provides the following benefits:

- Components are organized by their infrastructure function, making configs intuitive to navigate

- Teams can own specific layers using tools like GitHub's [`CODEOWNERS`](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners)

- Stacks can import only the layers they need (e.g., a dev environment might skip the monitoring layer)

- Changes to one layer don't affect other layers

## Example

The following example organizes infrastructure into three layers: **network**, **data**, and **compute**.

### Directory Structure

```console
   ├── stacks
   │   ├── catalog  # component defaults
   │   │   ├── vpc
   │   │   │   └── defaults.yaml
   │   │   ├── rds
   │   │   │   └── defaults.yaml
   │   │   └── eks
   │   │       └── defaults.yaml
   │   ├── layers  # infrastructure layers
   │   │   ├── network.yaml
   │   │   ├── data.yaml
   │   │   └── compute.yaml
   │   └── deploy
   │       ├── dev.yaml
   │       └── prod.yaml
   │
   └── components
       └── terraform
           ├── vpc
           ├── rds
           └── eks
```

### Configure atmos.yaml

**File:** `atmos.yaml`

```yaml
components:
  terraform:
    base_path: "components/terraform"

stacks:
  base_path: "stacks"
  included_paths:
    - "deploy/**/*"
  name_template: "{{.vars.stage}}"
```

### Define the Layers

Each layer imports the component defaults for that infrastructure function:

**File:** `stacks/layers/network.yaml`

```yaml
import:
  - catalog/vpc/defaults

# Network layer: VPC, subnets, security groups, etc.
```

**File:** `stacks/layers/data.yaml`

```yaml
import:
  - catalog/rds/defaults

# Data layer: databases, caches, message queues, etc.
```

**File:** `stacks/layers/compute.yaml`

```yaml
import:
  - catalog/eks/defaults

# Compute layer: EKS, ECS, Lambda, etc.
```

### Import Layers into Stacks

Stacks import the layers they need:

**File:** `stacks/deploy/dev.yaml`

```yaml
import:
  - layers/network
  - layers/data
  - layers/compute

vars:
  stage: dev

# Dev-specific overrides
components:
  terraform:
    rds:
      vars:
        instance_class: db.t3.small
```

**File:** `stacks/deploy/prod.yaml`

```yaml
import:
  - layers/network
  - layers/data
  - layers/compute

vars:
  stage: prod

# Prod-specific overrides
components:
  terraform:
    rds:
      vars:
        instance_class: db.r6g.xlarge
        multi_az: true
```

## Common Layer Examples

Here are typical infrastructure layers you might define:

| Layer | Components | Purpose |
|-------|-----------|---------|
| `network` | VPC, subnets, NAT, VPN | Foundation networking |
| `security` | WAF, security groups, KMS | Security controls |
| `data` | RDS, ElastiCache, S3 | Data storage |
| `compute` | EKS, ECS, EC2 | Application runtime |
| `observability` | CloudWatch, Datadog | Monitoring and logging |

## Selective Layer Import

Not every environment needs every layer. For example, a minimal dev environment might skip observability:

**File:** `stacks/deploy/dev.yaml`

```yaml
import:
  - layers/network
  - layers/compute
  # Skip observability in dev

vars:
  stage: dev
```

While production includes everything:

**File:** `stacks/deploy/prod.yaml`

```yaml
import:
  - layers/network
  - layers/security
  - layers/data
  - layers/compute
  - layers/observability

vars:
  stage: prod
```

## Layer Dependencies

Layers often have implicit dependencies. The compute layer depends on the network layer being provisioned first. Document or enforce this ordering in your deployment process:

```shell
# Deploy in layer order
atmos terraform apply vpc -s prod
atmos terraform apply rds -s prod
atmos terraform apply eks -s prod
```

## Related Patterns

- [Component Catalog](/design-patterns/component-catalog) - Define component defaults imported by layers
- [Mixins](/design-patterns/component-catalog/mixins) - Reusable configuration fragments
- [Component Overrides](/design-patterns/configuration-composition/component-overrides) - Apply overrides to all components in a layer

## References

- [Catalogs](/howto/catalogs)
- [Stack Imports](/stacks/imports)
