Shared launch capture is a platform primitive, not a RankWar feature
A multi-domain Laravel monolith gets stronger when waitlist and lead capture live in shared app and contact primitives, while product-specific tables keep their own mechanics.
The weak way to build a portfolio of apps is obvious in hindsight.
One product gets a waitlist. Another gets a lead form. A third gets a “request access” flow. Each one feels slightly different, so each one gets its own table, its own controller, its own little pocket of capture logic, and its own excuse for why the data cannot be shared cleanly later.
That path feels fast because the first product ships quickly.
It is also how a monolith turns into a graveyard of nearly identical intake flows that cannot compound.
The real platform boundary
Inside a multi-domain monolith, three things should not be confused:
- identity
- activation
- capture
Identity answers who the person is.
Activation answers which product they are allowed to operate.
Capture answers where attention was expressed before the rest of the product relationship was decided.
If those three collapse into one table or one side effect, the portfolio gets fuzzy fast. A signup starts masquerading as entitlement. A waitlist row starts pretending to be the canonical person record. Product-specific participation tables quietly become the only source of lead history.
That is weak systems design.
Why RankWar was the right place to fix it
RankWar already had public pressure, a creator cockpit, a shared contact spine, outbound email history, and first-party auth.
That made the gap impossible to ignore.
A public join on rankwar.app was clearly not just a product-specific event. It was also an expression of interest against an app-owned launch surface. Treating it as only a rankwar_entry meant the monolith was learning the competitive state of the campaign without learning the reusable capture state of the contact.
That is backwards.
The portfolio should learn the capture once. The product should add its own mechanics on top.
The contract that survives many apps
The durable shape is narrow:
- an app owns one or more launch-capture points
- a contact can attach to one of those points
- the submission may later map to a product-specific object
That lets the platform ask the right questions later:
- which launch surfaces actually convert?
- which contacts are showing up across campaigns or products?
- which surfaces generate pressure but weak follow-through?
- which product-specific mechanics are worth keeping because the shared capture truth says they produce better downstream behavior?
None of that requires flattening every product into the same generic schema.
That is the trap most teams fall into after they finally notice their tables are fragmented. They swing from siloed special cases into over-abstracted mush.
The winning move is smaller than that.
Keep shared primitives shared. Keep product tables explicit. Join them on purpose.
What most teams do instead
Most teams will choose one of three losing moves.
They will keep cloning waitlist tables because “it is just a small form.”
They will stuff everything into contacts.metadata and call it flexibility.
Or they will invent a massive generic campaigns abstraction that erases the fact that different products still have different mechanics.
All three fail.
The first one destroys compounding.
The second one destroys legibility.
The third one destroys product sharpness.
What this unlocks
Once capture is shared, the next moves get stronger:
- shared feedback primitives can hang off the same contact and app spine
- creators can see which campaigns or launches are attracting the right kind of attention
- one contact can matter across multiple launches without being rediscovered every time
- the monolith can support many domains without turning each new one into a clean-room project
That is the real point of the architecture.
Not “one codebase.”
One memory.
The rule
If your next app needs a waitlist, do not build another waitlist product inside the same monolith.
Build on the shared capture spine, then let the product add its own rules on top.
That is how a portfolio starts compounding instead of restarting.