Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 9 additions & 18 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,15 +270,15 @@ Pacta stores data in a `.pacta/` directory at your repository root.

```
.pacta/
── snapshots/ # Content-addressed snapshot storage
├── ab/ # First 2 chars of hash
── cdef1234... # Snapshot data (JSON)
── ...
├── refs/ # Named references to snapshots
── latest # Most recent snapshot
├── baseline # Baseline for comparison
── ...
└── config.json # Optional local configuration
── snapshots/
├── objects/ # Content-addressed snapshot storage
── a1b2c3d4.json # 8-char hash prefix filename
── e5f6a7b8.json
└── ...
── refs/ # Named references to snapshots
├── latest # Text file containing hash of most recent snapshot
── baseline # Text file containing hash (created with --save-ref)
└── ...
```

### Snapshots
Expand Down Expand Up @@ -313,12 +313,3 @@ Add to `.gitignore` only if you don't need persistent baselines:
# Ignore Pacta data (not recommended)
.pacta/
```

---

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `PACTA_NO_COLOR` | Disable colored output | `false` |
| `PACTA_DATA_DIR` | Override `.pacta/` location | `.pacta/` |
1 change: 1 addition & 0 deletions docs/examples/hexagonal-app.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--8<-- "examples/hexagonal-app/README.md"
1 change: 1 addition & 0 deletions docs/examples/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--8<-- "examples/README.md"
1 change: 1 addition & 0 deletions docs/examples/legacy-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--8<-- "examples/legacy-migration/README.md"
1 change: 1 addition & 0 deletions docs/examples/simple-layered-app.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--8<-- "examples/simple-layered-app/README.md"
11 changes: 11 additions & 0 deletions docs/stylesheets/extra.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* Mermaid diagram styling */
.mermaid {
text-align: center;
margin: 1rem 0;
}

/* Ensure mermaid diagrams are responsive */
.mermaid svg {
max-width: 100%;
height: auto;
}
8 changes: 6 additions & 2 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ Error: Baseline 'baseline' not found

2. Check that `.pacta/` directory exists and contains snapshots:
```bash
ls -la .pacta/snapshots/
ls -la .pacta/snapshots/objects/
ls -la .pacta/snapshots/refs/
```

3. If using CI, ensure the `.pacta/` directory is cached or committed
Expand Down Expand Up @@ -191,7 +192,10 @@ Coming soon:

### How do baselines work?

Baselines are content-addressed snapshots of your architecture at a point in time.
Baselines are content-addressed snapshots of your architecture at a point in time. They're stored in `.pacta/snapshots/`:

- **Objects** (`.pacta/snapshots/objects/`) - Immutable snapshot files named by 8-char hash
- **Refs** (`.pacta/snapshots/refs/`) - Named pointers (like `baseline`, `latest`) to object hashes

1. **Create baseline:** Saves current violations with a reference name
```bash
Expand Down
77 changes: 24 additions & 53 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -1,64 +1,35 @@
# Pacta Examples
# Examples

## simple-layered-app
Pacta includes several example projects demonstrating different architectural patterns and use cases.

A Python application demonstrating clean architecture with four layers:
## Available Examples

```
src/
├── ui/ # Controllers, API endpoints
├── application/ # Use cases, services
├── domain/ # Business logic, models
└── infra/ # Repositories, database
```

### Files

- `architecture.yml` - Defines layers and their file patterns
- `rules.pacta.yml` - Architectural rules (e.g., domain cannot depend on infra)

### Try it
| Example | Description | Best For |
|---------|-------------|----------|
| [Simple Layered App](simple-layered-app.md) | Classic N-tier architecture | Teams familiar with layered architecture |
| [Hexagonal Architecture](hexagonal-app.md) | Ports and Adapters pattern | Domain-driven design, high testability |
| [Legacy Migration](legacy-migration.md) | Baseline workflow for brownfield | Existing codebases, incremental adoption |

```bash
cd simple-layered-app

# Scan for violations
pacta scan src \
--model architecture.yml \
--rules rules.pacta.yml
## Quick Start

# Quiet mode (summary only)
pacta scan src \
--model architecture.yml \
--rules rules.pacta.yml -q
Each example includes:

# Verbose mode (all details)
pacta scan src \
--model architecture.yml \
--rules rules.pacta.yml -v
- `architecture.yml` - System and layer definitions
- `rules.pacta.yml` - Architectural constraints
- `src/` - Sample Python code demonstrating the architecture

# Save a baseline
pacta scan src \
--model architecture.yml \
--rules rules.pacta.yml \
--save-ref baseline
To run any example:

# Compare against baseline
pacta scan src \
--model architecture.yml \
--rules rules.pacta.yml \
--baseline baseline
```bash
cd examples/<example-name>
pacta scan . --model architecture.yml --rules rules.pacta.yml
```

# Save architecture snapshot (without running rules)
pacta snapshot save src \
--model architecture.yml \
--ref v1
## Creating Your Own

# Save another snapshot
pacta snapshot save src \
--model architecture.yml \
--ref v2
1. Copy the example closest to your needs
2. Modify `architecture.yml` to match your directory structure
3. Adjust `rules.pacta.yml` for your constraints
4. Run `pacta scan` and iterate

# Compare two snapshots
pacta diff src --from v1 --to v2
```
See the [Configuration Reference](../configuration.md) for full schema documentation.
95 changes: 95 additions & 0 deletions examples/hexagonal-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Hexagonal Architecture Example

This example demonstrates how to use Pacta to enforce [Hexagonal Architecture](https://alistair.cockburn.us/hexagonal-architecture/) (also known as Ports and Adapters).

## Architecture Overview

```mermaid
flowchart TB
subgraph Driving["Driving Side (Primary)"]
PA[Primary Adapters<br/>Controllers, CLI, Event Handlers]
end

subgraph Application["Application Core"]
IP[Inbound Ports<br/>Use Case Interfaces]
subgraph Domain["DOMAIN"]
D[Entities<br/>Domain Services]
end
OP[Outbound Ports<br/>Repository Interfaces]
end

subgraph Driven["Driven Side (Secondary)"]
SA[Secondary Adapters<br/>Database, APIs, Queues]
end

PA -->|uses| IP
IP -->|calls| D
D -->|uses| OP
SA -.->|implements| OP

style Domain fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
style D fill:#bbdefb,stroke:#1565c0
style IP fill:#fff8e1,stroke:#f57f17
style OP fill:#fff8e1,stroke:#f57f17
style PA fill:#f3e5f5,stroke:#7b1fa2
style SA fill:#e8f5e9,stroke:#2e7d32
```

## Directory Structure

```
src/
├── domain/ # Core business logic (center of hexagon)
│ ├── product.py # Domain entity
│ └── product_service.py # Domain service
├── ports/
│ ├── inbound/ # Driving ports (use case interfaces)
│ │ └── catalog_use_case.py
│ └── outbound/ # Driven ports (repository interfaces)
│ └── product_repository.py
└── adapters/
├── primary/ # Driving adapters (controllers, CLI)
│ └── api_controller.py
└── secondary/ # Driven adapters (database, APIs)
└── postgres_product_repository.py
```

## Key Rules

| Rule | Description |
|------|-------------|
| Domain → Adapters | **Forbidden** - Domain must not know about adapters |
| Domain → Outbound Ports | **Allowed** - Domain uses repository interfaces |
| Ports → Adapters | **Forbidden** - Ports are interfaces, adapters implement them |
| Primary Adapters → Domain | **Warning** - Should go through inbound ports |
| Secondary Adapters → Outbound Ports | **Allowed** - Implements the interface |
| Adapters → Adapters | **Forbidden** - Adapters should be independent |

## Usage

```bash
# Run architecture check
pacta scan . --model architecture.yml --rules rules.pacta.yml

# Expected output (clean architecture):
# ✓ 0 violations
```

## Dependency Flow

Dependencies always point **inward** toward the domain:

```mermaid
flowchart LR
PA[Primary<br/>Adapters] --> IP[Inbound<br/>Ports] --> D((DOMAIN))
SA[Secondary<br/>Adapters] --> OP[Outbound<br/>Ports] --> D

style D fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
```

This ensures:
- Domain is isolated and testable
- Adapters can be swapped without changing business logic
- The application is framework-agnostic
50 changes: 50 additions & 0 deletions examples/hexagonal-app/architecture.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
version: 1
system:
id: hexagonal-app
name: Hexagonal Architecture Example

containers:
main-app:
name: Main Application
description: |
Demonstrates Hexagonal Architecture (Ports and Adapters).
The domain is at the center, ports define boundaries,
and adapters connect to the outside world.
code:
roots:
- src
layers:
# Core business logic - the heart of the hexagon
domain:
name: Domain
description: Core business logic, entities, and domain services
patterns:
- src/domain/**

# Ports - interfaces that define how the domain communicates
ports-inbound:
name: Inbound Ports
description: Use case interfaces (driving/primary ports)
patterns:
- src/ports/inbound/**

ports-outbound:
name: Outbound Ports
description: Repository and external service interfaces (driven/secondary ports)
patterns:
- src/ports/outbound/**

# Adapters - implementations that connect to the outside world
adapters-primary:
name: Primary Adapters
description: Controllers, CLI, event handlers (driving adapters)
patterns:
- src/adapters/primary/**

adapters-secondary:
name: Secondary Adapters
description: Repository implementations, external API clients (driven adapters)
patterns:
- src/adapters/secondary/**

contexts: {}
Loading