Apex StemA thought-driven Apex development stack
Handler-Usecase Architecture +4 OSS libraries, born from 5 years of refactoring real-world chaos.
The Stack
Four OSS libraries, one architecture. Each can be adopted independently — or together as a unified stack.
ApexEloquent
SOQL/DML ORM with a mock-first testing API. Used for unit-testing the Usecase layer.
⚡ SELECT-omission detection via MockEntry
ApexBlueprint
Declarative test data factory. Used for integration-testing the Handler layer with real DML.
⚡ Layered topological sort for bulkified DML
GitHub →ApexTrace
Usecase lifecycle logging.Trace.of() with start / skip / finish / log.
⚡ TraceFlow-based behavior assertions in tests
GitHub →ApexTools
TriggerHandlerbase class and an HTTP request wrapper with DI support.
⚡ Field-change detection helpers for after-update handlers
GitHub →Plus a thin Result DTO convention for the LWC ↔ Apex boundary. Start with one library — start anywhere.
Handler-Usecase Architecture
Two layers. One rule per layer. The rest is up to you.
Handler Layer
Integration tests · ApexBlueprintEntry points — Trigger, Batch, REST, Flow, Schedulable. Absorbs the entry-point ceremony, then hands off to a Usecase.No business logic here.
Usecase Layer
Unit tests · ApexEloquent (mock DB)Business logic. Only invoke() is public. Constructor takes every dependency —no half-built objects.
Laravel-MVC philosophy
Define the trunk only. Reader / Validator / Mapper emerge from the business — they're never enforced.
Tests map 1-to-1
Handler → integration with real DML. Usecase → unit with mock DB. Same convention everywhere.
AI-friendly by design
The whole convention fits in 30 lines of CLAUDE.md. AI codegen doesn't drift — there's nowhere to drift to.
Get Started
You don't have to rewrite anything. Start with one query — grow from there.
Take the first step~2 minute read · then continue to the full guide
The Story
How a five-year-old Salesforce org turned into Apex Stem — one Usecase at a time.
The Challenge
▸A five-year-old Salesforce org, with trigger-on-trigger debt and assertions that didn't really assert.
▸fflib felt too heavy for a small team. Raw Apex felt too unstructured for the long run.
▸I wanted something AI coding assistants could actually follow — a convention small enough to fit in CLAUDE.md.
What emerged
▸Rebuilt incrementally — one Usecase, one mockable query at a time.
▸ApexEloquent came first. ApexBlueprint, ApexTrace, and ApexTools followed as the needs surfaced.
▸The stack you see today is what I'd want if I had to start the refactor over.