dangioffre's avatar

dangioffre liked a comment+100 XP

2mos ago

I have been building VerseDB (versedb.com) in Laravel for the past couple of years. It is a comic book database where users catalog collections, track pull lists, and contribute data through a community moderation system. I wanted to share some architectural decisions, what worked, and what I would do differently.

The stack is Laravel 12 on PHP 8.5, deployed on Laravel Cloud with PostgreSQL. Livewire 4, Filament 5 admin panel, and Horizon. Search is Meilisearch via Scout, Redis through Upstash, and Reverb for real-time library sync between web and mobile and for streaming AI responses. Spatie's permissions, tags, and activity log, Cashier for Stripe subscriptions, Nightwatch for monitoring, Prism PHP for AI features, and the level-up package for gamification.

Comic book data is deceptively complex. A "title" like Batman can have dozens of series across decades. Each series has issues. Each issue can have multiple variant covers. Characters appear across publishers. Story arcs span multiple series. Creators fill different roles on different issues. The data model ended up at 105 Eloquent models and 21 custom pivot models. The core hierarchy is Title, Series, Issue, with IssueVariant as a separate model rather than a column. Making variants their own model was one of the better early calls since they carry their own UPC, cover image, creator relationships, and market data. I love comics, but honestly it is the complexity of this domain that made me want to build it and keeps it fun to work on.

The community edit system lets users submit edits to any entity and those go through moderation before being applied. Each submission tracks a single field change rather than a full model diff. On approval, the system checks isFillable() on the target model before writing anything, which prevents updates to fields the model does not explicitly allow. An append-only history log tracks every approved change for auditing and showing contributors their impact.

With that many models and deep relationships, count queries get expensive fast. I went with denormalized count columns maintained by observers, with a nightly recalculation as a safety net. It works, but anything that bypasses Eloquent silently breaks the counts.

Also users can import collections from spreadsheets, with a pipeline that fuzzy-matches rows against the database and learns from user corrections. There is a gamification layer to incentivize data contributions, and on the marketplace side, sale records are aggregated into price snapshots per issue and grade to estimate fair market value.

Looking back, there are a few things I would approach differently. The moderation service handles too many concerns and should be broken into per-content-type handlers. Bulk operations and observers do not mix well, and I should either bust caches explicitly in bulk methods or move count maintenance entirely to the scheduled recalculation. Cache invalidation is split across observers, jobs, and scheduled commands with no unified strategy, which has caused stale data bugs.

I would love to hear any feedback on the site itself or the decisions outlined here.

dangioffre's avatar

dangioffre wrote a reply+100 XP

2mos ago

Building VerseDB has been a great journey and a big learning experience. I am happy to see how far it has come and how much it has grown. I look forward to continuing its development.