Google Drive as just another filesystem

The test is removal, not addition

Geschrieben von Timo Rieber am 5. Dezember 2025

mainzelmen renames, converts, and extracts data from files. It started on local directories, with each domain receiving a FileSystem protocol through a typed context. Adding Google Drive was the expected payoff: new class, same eight methods, no domain changes. The test I hadn't planned for came weeks later, when I removed the original adapter entirely.

The command that doesn't know

FileSystem is a Python Protocol - eight methods, no imports from infrastructure:

class FileSystem(Protocol):
    def list_files(self, directory: Path, pattern: str = '*') -> list[Path]: ...
    def read_file(self, path: Path) -> bytes: ...
    def write_file(self, path: Path, data: bytes) -> None: ...
    def rename_file(self, old: Path, new: Path) -> None: ...
    def delete_file(self, path: Path) -> None: ...
    def get_file_metadata(self, path: Path) -> FileMetadata: ...
    def copy_file_times(self, source: Path, target: Path) -> None: ...
    def file_exists(self, path: Path) -> bool: ...
Python

Each domain's Context holds a file_system: FileSystem field. The converter command reads a text file, generates a PDF, writes it, copies timestamps, deletes the original - five filesystem operations, all through ctx.file_system:

@dataclass(frozen=True)
class ExecuteConversionCommand:
    operations: list[ConversionOperation]

    def execute(self, ctx: Context) -> list[ConversionOperation]:
        for op in self.operations:
            text_data = ctx.file_system.read_file(op.source_path)
            pdf_data = ctx.pdf_generator.convert_text_to_pdf(
                text_data, author='mainzelmen', title=op.source_path.name
            )
            ctx.file_system.write_file(op.target_path, pdf_data)
            ctx.file_system.copy_file_times(op.source_path, op.target_path)
            ctx.file_system.delete_file(op.source_path)
Python

This code ran against LocalFileSystem for months. When GoogleDriveFileSystem took over - 157 lines of Drive API calls, per-folder caching, batch list operations - not a line changed.

What removal proves

The commit message read "Purge local filesystem integration." 46 files changed, over a thousand lines removed. Frontend components for directory browsing, upload routes, session handling for local paths, test fixtures wiring temporary directories - all gone.

Zero domain commands touched. Every execute(self, ctx: Context) method stayed identical.

Adding a backend tests whether the protocol is complete - whether eight methods cover every operation the domain uses. Removing one tests something harder: whether the protocol is a real boundary or a thin wrapper around the first implementation. A wrapper leaks assumptions into calling code - path separators, OS-specific errors, stat structures that only make sense on local disk. A domain-owned protocol defines what file operations mean in domain terms. The backends translate.

The acid test of an abstraction isn't whether you can plug in a second implementation. It's whether you can unplug the first one.

After the purge, InMemoryFileSystem - a dict-based fake satisfying the same protocol - still runs every domain test. No filesystem setup, no temp directories, no cleanup. The domain's ignorance is structural, not a matter of discipline.