Skip to content

Use Case 6: Multi-Model Service with Registration

Scenario: A social app manages users, posts, and comments. Each has its own model, repository, and indexes โ€” registered centrally and initialized at startup.


๐Ÿ“ฆ What's New?

Component Description
ModelRegistry.register One call per collection โ€” model + indexes together.
ModelRegistry.initialize_all Creates every registered index at startup (idempotent).
Repositories One repository class per collection, all sharing the same connection.

๐Ÿš€ Example

Python
from contextlib import asynccontextmanager

from fastapi import FastAPI
from mongo_ops import BaseDocument, BaseRepository, ModelRegistry, MongoConnectionManager


class User(BaseDocument):
    username: str = ""
    email: str = ""
    role: str = "user"


class Post(BaseDocument):
    title: str = ""
    content: str = ""
    author_id: str = ""
    likes: int = 0


class Comment(BaseDocument):
    post_id: str = ""
    user_id: str = ""
    content: str = ""


class UserRepository(BaseRepository[User]):
    def __init__(self):
        super().__init__("users", User)


class PostRepository(BaseRepository[Post]):
    def __init__(self):
        super().__init__("posts", Post)


class CommentRepository(BaseRepository[Comment]):
    def __init__(self):
        super().__init__("comments", Comment)


# ---------------------------
# Central registration + indexes
# ---------------------------
ModelRegistry.register("users", User, indexes=[("email", 1), ("username", 1)])
ModelRegistry.register("posts", Post, indexes=[("author_id", 1), ("created_at", -1)])
ModelRegistry.register("comments", Comment, indexes=[("post_id", 1), ("user_id", 1)])


# ---------------------------
# Lifecycle
# ---------------------------
@asynccontextmanager
async def lifespan(_app: FastAPI):
    async with MongoConnectionManager.lifespan(
        uri="mongodb://localhost:27017", db_name="social_app"
    ):
        await ModelRegistry.initialize_all()
        yield


app = FastAPI(lifespan=lifespan)


# Created after connect() โ€” inside the lifespan body is fine, or use a dependency.
user_repo = UserRepository()
post_repo = PostRepository()
comment_repo = CommentRepository()


@app.post("/users/", response_model=User)
async def create_user(user: User):
    return await user_repo.create(user)


@app.post("/posts/", response_model=Post)
async def create_post(post: Post):
    return await post_repo.create(post)


@app.post("/comments/", response_model=Comment)
async def create_comment(comment: Comment):
    return await comment_repo.create(comment)

๐Ÿ’ก Tips

  • Place user_repo = ... inside the lifespan/after connect. Module-level instantiation before connect() raises RuntimeError("Database not connected...").
  • Model relationships here are plain ObjectId strings stored on the child docs. To resolve them on read, see use case 08 โ€“ Population.
  • Registering indexes on created_at/author_id/post_id keeps the common queries indexed (see use case 13).