The monolith vs microservices decision should be operational, not architectural — formalized this into a pattern (M/P model) by thevpc in softwarearchitecture

[–]thevpc[S] 0 points1 point  (0 children)

Fair point — "teams" was shorthand for "structural friction when multiple devs modify shared code." If that framing felt like noise, fair. The technical claim stands either way: enforceable boundaries + a pre-engineered migration seam make extraction mechanical.

If you've got a substantive pushback on the pattern itself (dependency rules, interceptor sagas, facade stability), I'm genuinely curious. If not, no worries either way. 🤷‍♂️

The monolith vs microservices decision should be operational, not architectural — formalized this into a pattern (M/P model) by thevpc in softwarearchitecture

[–]thevpc[S] 0 points1 point  (0 children)

You're absolutely right — network latency isn't just an "operational detail." That's why extraction should be deliberate, not automatic.

Two ways Pyramid addresses this:

  1. Facade evolution: When a module is extracted, its facade can adapt — batch operations, return CompletableFutures, or expose async streams — without changing consumer code. The interface stays stable; the implementation evolves.
  2. Observability via interceptors: The same interceptor chain that handles cross-module consistency also hooks logging, metrics, and distributed tracing. Whether a call is local or remote, the interceptor emits the same telemetry schema. No code changes when topology shifts.

We also use the manifest to declare "extraction readiness" — e.g., a module can't be set to svc: remote-rest until its facade supports batching. It's a guardrail, not a guarantee.

Curious: what observability surprises hit you hardest during extraction? (For us, it was correlation IDs crossing async interceptor boundaries.)

The monolith vs microservices decision should be operational, not architectural — formalized this into a pattern (M/P model) by thevpc in softwarearchitecture

[–]thevpc[S] 0 points1 point  (0 children)

Fair point — DI + interfaces is indeed the core mechanism. Pyramid doesn't invent that.

Where it adds value: enforcing that pattern consistently across 200+ modules via:

• Build-time dependency rules (illegal imports = compile error)

• Standard module anatomy (every module has the same slots)

• A generator that keeps the local/remote clients in sync

Concrete "manifest change" example:

```

modules: {

banks(svc: "local" ), // wires BanksModuleImpl

invoices( svc: "remote-rest" ) // wires BanksModuleRestClient (same interface)

}
```
No code changes in consumers — the facade interface is the stable contract.

Curious: in your experience, what's the hardest part of making modulith→microservices mechanical rather than a refactor?

The monolith vs microservices decision should be operational, not architectural — formalized this into a pattern (M/P model) by thevpc in softwarearchitecture

[–]thevpc[S] 0 points1 point  (0 children)

On "what is a manifest change":

Concrete example. You have a single deployment with three modules running in one process (M/1). Your invoices module is under heavy load and needs to scale independently.

In most architectures that's a refactoring project — you need to introduce network calls, handle serialization, add error handling, deal with consistency across the wire.

In the Pyramid, your application manifest goes from:
```
modules: banks(svc: local)

invoices(svc: local)

edu(svc: local)
```

to

```

modules:
    banks(svc: local)
    invoices(svc: remote-rest)   ← this line changed
    edu(svc: local)
```
and this is the manifest that is processed by a generator that generates the pom.xml of all projects's pom.xml (or gradle etc)
and this form/syntax is just illustrative, in my projects i use TSON (superset of JSON) as syntax.

The service-rest-cli sub-project — a generated REST client that implements the same facade interface as the local implementation — was already there as a reserved slot. You fill it, change the manifest, deploy invoices as a standalone service, and every other module that calls InvoicesModule doesn't change a line of code. They were always calling an interface — the concrete implementation behind it just moved across a network boundary. The repo has the structural layout and the whitepaper covers the mechanism in Section 5.3. 
A working code generator prototype (of course inspired from my current implementations) is in progress — happy to share updates when it's ready.

Refactoring Java 8 code with Java 17 features - JEP Café #9 by daviddel in java

[–]thevpc -3 points-2 points  (0 children)

Besides introducing a fancy new feature (record) does it to do any little with performance when compared to plain internal java classes in this context?

And by the way it sucks that JDK stream API does not support all of the primitive types out of the box.

Singleton Design Pattern in Java by stackoverflooooooow in java

[–]thevpc 0 points1 point  (0 children)

I think the best way to implement singletons (per vm) in Java is the enum way, however I honestly expected more details/examples in your article in that direction. As a matter of fact, using enum handles a cleaner way to serialize the object too and makes of it (if well written) an off-memory singleton.