Skip to content

Saga Compensation in Action

FennFlow's Unit of Work automatically rolls back all pending operations if something goes wrong. Combined with auto_commit=False, you get full control over when a transaction is committed — making it trivial to tie file storage to the result of an external service call.

Example: Upload avatar and verify with external face verification API

import asyncio

import httpx

from fennflow import ConfigDict, UnitOfWork
from fennflow.backends import SqlalchemyBackendConfig
from fennflow.connectors import S3ConnectorConfig
from fennflow.files import BinaryContent, ImageContent
from fennflow.repositories import (
    CreateRepository,
    GeneratePresignedUrlRepository,
    S3RepoField,
)


class AvatarRepository(CreateRepository, GeneratePresignedUrlRepository):
    pass


class AppUOW(UnitOfWork):
    avatars = S3RepoField(AvatarRepository, bucket_name="avatars")
    config = ConfigDict(
        backend=SqlalchemyBackendConfig(),
        connector=S3ConnectorConfig(
            endpoint_url="https://s3.amazonaws.com",
            aws_access_key_id="aws-key",
            aws_secret_access_key="aws-secret",
        ),
    )


async def process_avatar(user_id: str, avatar: ImageContent) -> None:
    async with AppUOW(auto_commit=False) as uow:
        await uow.avatars.at(user_id).create(avatar)

        presigned_url = await uow.avatars.generate_presigned_url(avatar.filename)

        async with httpx.AsyncClient() as client:
            response = await client.post(
                "https://verify.example.com/face",
                json={"url": presigned_url},
            )

        if response.is_success:
            await uow.commit()
        # if verification failed or an exception was raised,
        # the UoW rolls back on exit — avatar is removed from S3 automatically


async def main():
    avatar = BinaryContent.from_local_path("avatar.jpg")
    await process_avatar(user_id="user-123", avatar=avatar)


if __name__ == "__main__":
    asyncio.run(main())

No manual cleanup code. No try/finally. If the verification API rejects the image or raises an exception, the UoW exits without a commit and the uploaded avatar is automatically compensated out of S3. You can use auto_commit=True and call uow.rollback on failure as well.