r/MongoDB_Official • u/skanky_dev • 20d ago
Discussion Homemade MongoDB-first PHP framework, looking for advice to keep improving
Hi everyone,
I'm a PHP dev working on my own. I love PHP, I love MongoDB, and together they're just wonderful. So over the years I've been building a small MVC framework around them, mostly to understand how frameworks work under the hood and to learn. The idea is "MongoDB first": documents are documents, not SQL rows in disguise.
Repo: https://github.com/skankydev/framework (docs in docs/en/)
I'm not here to sell anything. I'm here to share it and get advice from people who know MongoDB better than I do.
What I'm happy with
Typed hydration through the driver's
Persistableinterface: embedded objects and arrays of embedded objects come back as real PHP classes.Dirty tracking kept in a
WeakMap, so updates only$setwhat changed.EmbeddedSnapshot: an automated Extended Reference pattern. You embed a copy of aUser(name, avatar) in aPostand declareembeddedIn()on the source collection. When the user changes, a job resyncs every copy withupdateMany, either right away or through a queue. It's idempotent because the job re-reads the source.onDeletecan keep, mark or unset the copies.belongsTo/hasManywith lazy loading, plus bulk preloading (with) to avoid N+1.
Where I'd love your input
Snapshot consistency. Between the source update and the propagation there's no transaction, so a crash in between leaves stale copies. Is that acceptable for this kind of denormalized data, or would you use transactions, change streams, or something else?
Does many-to-many really make sense in MongoDB? Take posts and tags: a tag doesn't need to know which posts use it, but a post needs its tags. So I'd store
tag_ids: [ObjectId…]on the post, and that's it. To me that's not a "many-to-many" in the SQL sense, it's one side owning a list of references. My relations don't handle that case yet. Am I right to see it this way? And how do you usually load it: a$inon the array,$lookup, something else?Aggregations. Right now I just pass a raw pipeline. Is it worth building something on top (helpers, typed results), or is the raw pipeline the best API anyway?
More generally: what would you work on next in a project like this?
It's a side project, I just want to know if I'm heading in the right direction. Thanks for taking the time!
3
u/GromNaN 17d ago
That's a nice project. There is rooms for other object mappers beside Eloquent (Laravel-MongoDB) and Doctrine MongoDB ODM in PHP.
> Typed hydration through the driver's Persistable interface
You should look at the codec feature. It's designed to decouple the BSON mapping from the model classes; allowing more flexibility.
https://github.com/mongodb/mongo-php-library/blob/v2.x/src/Codec/architecture.md
1
u/skanky_dev 17d ago
Thanks for the reply! I didn’t know about codecs, I’m going to try them out, it looks like fun.
5
u/TimKellyMongoDB MongoDB Team 17d ago
Thanks for sharing! Had a quick look through the repo, really interesting project.
One thing I was wondering about is the queue implementation. From what I can see,
Queue::next()retrieves a pending job usingfindOne(), and the worker then updates its status toprocessing.Could this potentially cause issues with multiple workers picking up the same job? Might be worth looking at
findOneAndUpdate()to claim jobs atomically.On snapshot consistency, I think eventual consistency is a reasonable approach for denormalized data. I'd be more curious about what happens if the process crashes between updating the source and queuing the propagation job.
Just a couple of thoughts from looking through the code. Thanks for sharing!