ArchML is a text-based DSL for defining software architecture alongside your code. Architecture files live in the repository, are version-controlled like any other source file, and stay in sync with the system they describe.
The core idea: define your architecture once as a model, then derive multiple views from it — interactive web diagrams, consistency reports, and embedded Sphinx documentation — without maintaining separate diagrams per tool.
Architecture documentation drifts. Visual tools like Enterprise Architect or ArchiMate live outside the codebase, so diagrams rot while the code moves on. Lightweight alternatives like Mermaid embed diagrams in Markdown, but each diagram is standalone — there is no shared model, no cross-diagram consistency, and no drill-down navigation.
ArchML sits between these extremes:
- Text-first —
.archmlfiles are plain text, stored in git, reviewed in pull requests. - Model-based — one model, many views. Define a component once; reference it everywhere.
- Consistency checking — the tooling catches dangling references, ports missing
connectorexpose, and type mismatches across channels. - Navigable views — drill down from system landscape to individual component internals.
- Sphinx-native — embed live architecture views directly in your documentation.
A small e-commerce backend expressed in ArchML:
// types.archml
type OrderItem {
field product_id: String
field quantity: Int
field unit_price: Decimal
}
enum OrderStatus {
Pending
Confirmed
Shipped
Delivered
Cancelled
}
interface OrderRequest {
field order_id: String
field customer_id: String
field items: List<OrderItem>
}
interface OrderConfirmation {
field order_id: String
field status: OrderStatus
field confirmed_at: Timestamp
}
interface PaymentRequest {
field order_id: String
field amount: Decimal
field currency: String
}
interface InventoryCheck {
field product_id: String
field quantity: Int
}
// systems/ecommerce.archml
from types import OrderRequest, OrderConfirmation, PaymentRequest, InventoryCheck
system ECommerce {
title = "E-Commerce Platform"
description = "Customer-facing online store."
user Customer {
provides OrderRequest
requires OrderConfirmation
}
component OrderService {
title = "Order Service"
description = "Accepts, validates, and processes customer orders."
component Validator {
requires OrderRequest
provides ValidationResult
}
component Processor {
requires ValidationResult
requires PaymentRequest
requires InventoryCheck
provides OrderConfirmation
}
// Internal wiring: Validator feeds Processor via an implicit channel
connect Validator.ValidationResult -> $validation -> Processor.ValidationResult
// Remaining ports must be explicitly exposed at the OrderService boundary
expose Validator.OrderRequest
expose Processor.PaymentRequest
expose Processor.InventoryCheck
expose Processor.OrderConfirmation
}
component PaymentGateway {
title = "Payment Gateway"
tags = ["critical", "pci-scope"]
provides PaymentRequest
}
component InventoryManager {
title = "Inventory Manager"
provides InventoryCheck
}
// Wire customer to order pipeline
connect Customer.OrderRequest -> $order_in -> OrderService.OrderRequest
connect OrderService.OrderConfirmation -> $order_out -> Customer.OrderConfirmation
// Wire OrderService to backing services
connect PaymentGateway.PaymentRequest -> $payment -> OrderService.PaymentRequest {
protocol = "gRPC"
async = true
}
connect InventoryManager.InventoryCheck -> $inventory -> OrderService.InventoryCheck {
protocol = "HTTP"
}
}
Large architectures split naturally across files. A from ... import statement brings named definitions into scope; use component X places an imported component inside a system without redefining it. Its exposed ports are available as Entity.port_name in connect and expose statements. Remote repositories can be referenced with @repo-name prefixes for multi-repo workspace setups.
| Keyword | Purpose |
|---|---|
system |
Group of components or sub-systems with a shared goal |
component |
Module with a clear responsibility; may nest sub-components |
user |
Human actor (role or persona) that interacts with the system |
interface |
Named contract of typed data fields; supports @v1, @v2 versioning |
type |
Reusable data structure (used within interfaces) |
artifact |
Abstract data artifact (file, directory, blob, etc.) used as a field type |
enum |
Constrained set of named values |
field |
Named, typed data element with optional description and schema |
requires / provides |
Declare a port that consumes or exposes an interface |
requires X as port |
Assign an explicit name to a port |
connect A.p -> $ch -> B.p |
Wire two ports via a named implicit channel |
connect A.p -> B.p |
Wire two ports directly (no named channel) |
expose Entity.port [as name] |
Explicitly surface a sub-entity's port at the enclosing boundary |
external |
Marks a system, component, or user as outside the development boundary |
from … import |
Bring specific definitions from another file into scope |
use component X |
Place an imported entity inside a system |
tags |
Arbitrary labels for filtering and view generation |
Primitive types: String, Int, Float, Decimal, Bool, Bytes, Timestamp, Datetime
Container types: List<T>, Map<K, V>, Optional<T>
Multi-line descriptions use triple-quoted strings:
description = """
Accepts and validates customer orders.
Delegates payment to PaymentGateway.
"""
Enum values each occupy their own line — no commas needed.
Full syntax reference: docs/LANGUAGE_SYNTAX.md
pip install archmlOr with uv:
uv add archmlInitialize a new ArchML workspace. Creates .archml-workspace.yaml in the given directory with <name> as the workspace identity.
archml init my-service .Parse and validate all .archml files in the workspace. Reports dangling references, unused interfaces, and other consistency errors. Exits with a non-zero status if any errors are found.
archml check
archml check /path/to/workspaceRender a box diagram for a system or component and write it to a file. The entity path uses :: as a separator for nested elements.
archml visualize ECommerce diagram.svg
archml visualize ECommerce::OrderService order_service.pngNote
This command exists, but is not yet working properly.
Launch an interactive web-based architecture viewer. Opens a browser UI for exploring the full architecture with drill-down navigation.
archml serve
archml serve --port 9000
archml serve --host 0.0.0.0 --port 8080Note
This command exists, but is not yet working properly.
Resolve branch or tag references in the workspace configuration to their latest commit SHAs and write them to the lockfile (.archml-lock.yaml). Run this to update pinned revisions.
archml update-remoteDownload remote git repositories to the local sync directory at the commits pinned in the lockfile. Run update-remote first if the lockfile does not exist yet.
archml sync-remoteArchML is in early development. The functional architecture domain (systems, components, interfaces, ports, and channels) is implemented. Behavioral and deployment domains are planned.
See docs/PROJECT_SCOPE.md for the full vision and roadmap.
Apache 2.0