RSS Amplifier

Andreas Fertsch-Röver · Jun 28, 2026

BCE: The Project Structure I Trying Next

0
Sign in to vote or save

Andreas Fertsch-Röver · Andreas Fertsch-Röver

It rarely starts as a textbook "everything sorted by technical layer" disaster. In my projects I actually tried to keep the business visible: packages named after what the code does, not how it’s built. The intent was there.

But it doesn’t hold. Slowly the technical vocabulary creeps back in and starts sitting next to the business one: a predicate here, a validator there, a Camel route, a service, an exception, a transformer. Each addition feels reasonable on its own. A few months later the business structure is diluted. The domain packages are half-business, half-plumbing, and the line between "what this package is" and "how it happens to be wired" is gone.

The result is the same as pure layering, just arrived at more honestly: the code for a single capability is smeared across business intent and technical machinery, and nobody can hold a feature in their head, or increasingly in an LLM’s context window.

There’s an alternative I keep seeing Adam Bien evangelize, and after enough painful merges I’m convinced it’s worth trying on my next project: BCE.

Why Good Intentions Drift

The problem isn’t that anyone decides to bury the business. It’s that there’s no rule about where technical concerns belong, so they land wherever is convenient, usually right next to the domain code they touch. Without a structural boundary, drift is the default.

The cost compounds:

  • Diluted intent — a domain package fills up with predicates, validators, transformers, and exceptions until "what this is about" is no longer obvious from the names.

  • Hidden coupling — a service quietly reaches into another component’s internals, and nothing structural stops it.

  • No clear ownership — where does a new validator or route go? Whatever package the author guessed, which is rarely the same as last time.

The structure groups things that change at different times and separates things that change together. That’s backwards.

Where BCE Comes From

BCE (Boundary-Control-Entity, also written ECB) isn’t new. It originates with Ivar Jacobson and his use-case-driven design: every use case is realized by objects in three roles, a boundary the actor interacts with, a control that coordinates the work, and entities that hold the domain state.

Adam Bien took that analysis-level idea and made it a code-level structuring pattern for Jakarta EE, MicroProfile, and Quarkus. He kept the three roles and dropped the ceremony, turning BCE into a pragmatic convention for how you lay out packages and where each kind of class belongs. His reference for it lives at bce.design.

The Core Flip: Vertical Slices, Not Horizontal Layers

The key move is rotating the structure 90 degrees. Instead of grouping by technical layer, BCE groups vertically by business capability.

The application becomes a set of business components (orders, catalog, cart, pricing, checkout), and each component contains its own boundary, control, and entity. Cohesion lives inside a component; coupling between components is kept deliberately small.

Boundary

The component’s face to the outside world: JAX-RS resources, GraphQL endpoints, message handlers. Input validation and coarse-grained operations live here.

Control

The orchestration and business logic: stateless procedural code, often interfaces with static methods rather than stateful classes.

Entity

The domain model: domain objects, persistence-mapped entities, value objects. Entities carry both state and behavior, not anemic data bags.

Dependencies Flow Inward

Inside a component, direct calls run strictly one way:

Boundary talks to Control, Control talks to Entity, Entity talks to no one.

Reverse communication doesn’t break that rule; it uses domain events. An entity emits an event instead of calling a control; a control publishes an event instead of invoking a boundary. Dependencies always point inward.

Across components, the rules stay strict: you reach another component only through its boundary or control, never by touching its entities, which are private internals. A direct call is tolerated, but a domain event is the preferred, loosely coupled path. The governing principle is minimal coupling between components, maximal cohesion within.

How It Looks on Disk

The package convention is predictable:

[organization].[project].[component].[boundary|control|entity]

For organization com.example and project shop:

com.example.shop.orders.boundary    // OrdersResource (JAX-RS), facades, health checks
com.example.shop.orders.control     // business logic, stateless procedural code
com.example.shop.orders.entity      // Order, OrderLine, @Entity classes, enums

A few of Bien’s own conventions on top of plain BCE:

  • Business components are named after their domain responsibility, never a technical concern. boundary / control / entity packages exist only inside a component.

  • Name classes after what they do. Avoid meaningless suffixes like *Impl, *Service, *Manager. It’s Order, not OrderEntity. The *Resource suffix survives only because it’s the JAX-RS idiom (and stays plural: OrdersResource).

  • Cross-cutting code lives in the root application package, not inside a component.

Note

Some third-party BCE tutorials enforce *Service and *Entity suffixes via ArchUnit. Bien’s conventions deliberately reject those: classes are named after responsibility, not their layer.

Components Are Packages, Not Modules

The thing I expected to be true and isn’t: that each component needs its own Maven module. Coming from a microservices mindset it’s an easy assumption, and Maven’s own "module" naming nudges you straight into it.

No. In Bien’s approach a business component is a package, a vertical slice, not a separate module. The whole application stays a single deployable unit: one WAR or one Quarkus app, with components sitting side by side as packages. His long-standing line: "WAR is the new EAR." You get modularity from package boundaries without dragging in multi-module build complexity.

Extra Maven modules appear only for test isolation: system tests go in a dedicated -st module, never for splitting business components.

Why This Fits LLM-Assisted Development

Here’s what pushed BCE from "nice idea" to "trying it next" for me.

A coding agent is bounded by what it can reason about at once. Horizontal layering is hostile to that: to change one feature the model has to load fragments from controllers, services, and repositories, infer the implicit wiring between them, and hope it didn’t miss a caller. Every feature is a scavenger hunt across the tree.

A BCE component is the opposite: self-contained. The boundary, the logic, and the domain model for one capability sit in one place with explicit, one-directional dependencies. That gives an agent three things it badly needs:

  • A bounded context — the whole capability fits in a window, so the model reasons over a complete unit instead of guessing at off-screen code.

  • A deterministic place to put code — new boundary code goes in boundary, logic in control, model in entity. No "where should this live?" ambiguity for the model to get wrong.

  • Enforceable rails — because BCE is convention-based, you guard it with ArchUnit tests: only boundary → control → entity, no cycles between slices, @Entity only in entity, @Transactional only in the boundary. The agent’s output gets checked against the architecture automatically.

This is exactly the bet behind Bien’s AIRails, "put your AI on rails." Its central rule is spec-driven BCE: one capability spec = one business component. Each requirement maps to exactly one bounded component, so the agent always has a deterministic home for generated code. BCE and AI-assisted development reinforce each other: the architecture gives the model the structure it needs to stay correct.

Worth Trying

I’m not retrofitting BCE into anything mid-flight. But for the next greenfield project the case is strong: a structure that keeps features cohesive, keeps coupling honest, ships as one deployable, and almost as a bonus happens to be the shape an LLM reasons about best.

Old idea from Jacobson, pragmatically sharpened by Bien, and newly relevant in the age of coding agents. That’s a combination worth betting a project on.

Read the original on fertsch-roever.com

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.