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.
