The Computing Series

How It Works

Consider a storage abstraction:

interface BlobStore:
    method put(key: String, data: Bytes) -> Result
    method get(key: String) -> Result<Bytes>
    method delete(key: String) -> Result
    method exists(key: String) -> Boolean

Two implementations:

class LocalFileBlobStore implements BlobStore:
    method put(key, data):
        path = join(base_dir, key)
        write_file(path, data)
        return Success

    method get(key):
        path = join(base_dir, key)
        if not file_exists(path):
            return NotFound
        return read_file(path)
class S3BlobStore implements BlobStore:
    method put(key, data):
        s3_client.put_object(bucket, key, data)
        return Success

    method get(key):
        try:
            return s3_client.get_object(bucket, key)
        catch NoSuchKey:
            return NotFound

Callers depend on BlobStore. The choice of local disk or S3 is a deployment decision invisible to callers. Testing uses LocalFileBlobStore or an in-memory stub. Production uses S3BlobStore. The abstraction holds.

Where does it break? If S3BlobStore.put occasionally returns EventuallyConsistent — an S3-specific result type — and callers start to handle it, the abstraction has leaked. Now callers know about S3. Removing S3 requires changing callers. The interface has become a lie.

Read in the book →