standr/architecture

Structural design

AI generates technically layered backends, but the center of gravity is always services/. Everything lands there because the model has no opinion about where behavior should live. The architecture plugin adds that opinion.

10 skills

/scan

Diagnose structural problems and recommend which skills to apply.

/place

Decide where new functionality should live in the backend.

/model

Classify a new concept: entity, value object, service, policy, workflow, or helper.

/align-analogy

Find analogous existing concepts and design in parallel with them.

/boundary

Inspect separation of responsibilities across layers and modules.

/hierarchy-check

Verify that concept hierarchy and abstraction levels are consistent.

/symmetry-check

Check whether similar backend concepts are represented similarly.

/scale-path

Pressure-test a design against the next 3–5 likely use cases.

/pattern-fit

Suggest a design pattern only when it genuinely clarifies the structure.

/doctrine

Core architectural philosophy loaded as context for all other skills.

Demo

Guided tour: coffee shop pricing

Same domain, two designs. Click any file to see its code. Start the tour to walk through what changes — and why.

without standr
coffee/
tests/
coffee/pricing.py
from .menu import MENU
 
 
def calculate_price(order: dict) -> float:
coffee_type = order.get("coffee")
if coffee_type not in MENU:
raise ValueError("unknown coffee")
 
price = MENU[coffee_type]
extras = order.get("extras", [])
 
for extra in extras:
if extra == "milk":
price += 0.2
elif extra == "oat":
price += 0.35
elif extra == "vanilla":
price += 0.4
elif extra == "caramel":
price += 0.45
elif extra == "double_shot":
price += 0.8
else:
raise ValueError(f"unsupported extra: {extra}")
 
if order.get("size") == "large":
price += 0.6
elif order.get("size") == "small":
price -= 0.2
 
if order.get("happy_hour"):
price *= 0.9
 
return round(price, 2)
with standr
coffee/
domain/
tests/
coffee/pricing.py
from .domain.coffee import Coffee, Size
from .domain.complement import Complement
from .domain.order import BaseCoffeeOrder, CoffeeOrder, ComplementDecorator, DiscountDecorator
 
_SIZE_MAP: dict[str, Size] = {
'small': Size.SMALL,
'regular': Size.REGULAR,
'large': Size.LARGE,
}
 
 
def build_order(raw: dict) -> CoffeeOrder:
size = _SIZE_MAP.get(raw.get('size', 'regular'), Size.REGULAR)
coffee = Coffee.from_menu(raw['coffee'], size)
 
order: CoffeeOrder = BaseCoffeeOrder(coffee)
 
for extra in raw.get('extras', []):
order = ComplementDecorator(order, Complement.from_catalog(extra))
 
if raw.get('happy_hour'):
order = DiscountDecorator(order, factor=0.9)
 
return order
 
 
def calculate_price(raw: dict) -> float:
return round(build_order(raw).price(), 2)

The before version is not wrong.

The procedural version works correctly and is easy to follow for the current requirements. The entity-based version is better for a narrower but more important reason: adding a new complement or discount policy is isolated. The difference is not “wrong vs. right”—it’s “works now vs. designed for what comes next.”

Install

Choose your method.

$ npx standr install