Prism
v0.1 · pre-release · Apache-2.0Topology is a compile flag.
Develop one modular monorepo against distributed semantics. Verify its boundaries on every commit. Compile it — deterministically — to a monolith or to microservices. Same code, same tests, both shapes.
$ mvn prism:compile -Dprism.topology=services
The problem
“We'll split it later” is a promise five years of coupling always breaks.
Teams correctly start with a monolith and plan to extract services when scale demands it. Then the coupling accumulates, and “later” arrives as a two-year rewrite. The monolith-vs-microservices decision gets made years before the information needed to make it exists — and unmaking it costs enormously.
Prism makes the promise enforceable. Boundaries are declared, not inferred — and a verifier guarantees in CI that they hold. Deployment topology becomes a reversible, per-environment compile flag instead of a bet placed on day one.
How it works
Distributed semantics from day one. Running as a monolith is free.
Declare modules and contracts
A module is a package. A contract is its only public surface — and it must admit failure by type. The verifier rejects anything else.
@Contract public interface PaymentContract { @Deadline(millis = 2000) Result<Receipt> charge(ChargeRequest request); }
Call it like plain Java
Inject the interface. The Result type forces you to handle failure — which is exactly what makes the same code correct over a network.
return payment .charge(new ChargeRequest(customerId, amount)) .map(receipt -> placeOrder(receipt)) .fold( order -> accepted(order), failure -> switch (failure.kind()) { case TIMEOUT, UNAVAILABLE -> retryLater(); case REJECTED -> tellUser(); // ... });
Verify on every commit
Six deterministic rules fail the build on boundary violations — including transactions that span modules. Rejected, not “handled”.
ERROR [transaction-containment] OrderService.java:42 Transactional method OrderService.place calls contract PaymentContract of module 'payment' — transactions must not span module boundaries
Compile to either topology
One deployable for dev and small scale. N services — with generated HTTP bindings, Dockerfiles, and compose — when a module needs its own life. Byte-identical on every run.
modules: order: { port: 8081 } payment: { port: 8082, deadline-ms: 2000 } packs: - builtin:gateway - builtin:observability
What you get
A compiler, a verifier, and an honest set of escape hatches
Declared boundaries
Modules are packages you declare, not guesses a tool makes. Verification is decidable; inference never was.
Fallible by type
Cross-module calls return Result<T> in both topologies. In monolith mode the failure branches simply never fire.
Six verification rules
Boundary integrity, contract fallibility, transaction containment, data ownership, event honesty, declaration sanity — in CI, deterministic.
Deterministic compiler
Same monorepo, byte-identical output — enforced by compile-twice-compare-bytes tests in Prism's own suite.
Template packs
Gateway, observability, Kafka — expressed in a structural template language anyone can extend. The built-ins contain zero Java.
The customization ladder
Annotations → overlays → runtime bindings → ejection. Every escape hatch lives in the monorepo; generated output is never hand-edited.
The guarantee
You were writing distributed-safe code all along.
The classic monolith-to-microservices failure is that the semantics change under the code: in-process calls can't time out, can't partially fail, and are transactional for free. Prism flips the abstraction — you always code against distributed semantics, and the in-process binding is just an execution optimization that happens to never fail.
Kill a service's dependency and it stays up, returning the failure your code already handles — because the type system made you handle it on day one.
// monolith: local dispatch — never fails // services: HTTP + deadline — can fail Result<Receipt> result = payment.charge(request); // either way, you wrote the failure path: result.fold(ok -> ship(), failure -> degrade());
Develop like a monolith.
Deploy like microservices.
Open source, Apache-2.0. Java 21+. The verifier alone is worth the install — adopt it without ever compiling.