- Author: Amin Boulouma, Software Engineer
- Github source code: https://github.com/aminblm/ai_systems_design_from_scratch
- Engineering Blog: https://aminblm.github.io/ai_systems_design_from_scratch/blog/
The Disjoint Base: Architectural Mutually Exclusive Types
In large-scale Python systems, inheritance is a powerful tool, but it is often abused. The “midnight deployment spike” frequently arises from ambiguous type hierarchies—where a class inadvertently inherits from two base systems that were never meant to coexist. This leads to runtime conflicts and “Heisenbugs” in your business logic.
The @disjoint_base decorator (from typing_extensions) provides a formal mechanism to enforce architectural mutual exclusivity. It allows you to define “poles” in your inheritance tree that cannot be combined, ensuring your class hierarchy remains flat, predictable, and clean.
The Theory: Enforcing Architectural Boundaries
When you mark a class as @disjoint_base, you are signaling to the type checker: “This class represents a foundational category, and it is architecturally incompatible with other foundational categories.” The type checker enforces this at build time, preventing the creation of “God Classes” that try to be everything at once.
Glossary for Beginners
- Disjoint: Two sets that have nothing in common. If they are disjoint, an object cannot be an instance of both at the same time.
- TypeAlias: A simple way to give a name to a complex type (like naming a blueprint).
- Unreachable Code: Code that the computer knows for a fact will never run (e.g., checking if an object is both
Disjoint1andDisjoint2when they are disjoint). - Inheritance Tree: The family tree of your classes.
Simple Implementation: Enforcing Separation
This prevents the accidental creation of classes that blend two incompatible domains.
from typing_extensions import disjoint_base
@disjoint_base
class PaymentMethod: pass
@disjoint_base
class ShippingMethod: pass
# This is allowed:
class CreditCard(PaymentMethod): pass
# This will trigger a Type Checker error:
class IllegalCombo(PaymentMethod, ShippingMethod): pass
Complex Implementation: TypeAlias for Domain Integrity
Combining disjoint_base with TypeAlias allows you to define clear domain boundaries that your entire team must follow.
from typing_extensions import disjoint_base, TypeAlias
@disjoint_base
class InternalService: pass
@disjoint_base
class ExternalService: pass
# Define a clean alias for your domain logic
ServiceCategory: TypeAlias = InternalService | ExternalService
def route_request(service: ServiceCategory):
# Static analyzers now know exactly which branch is reachable
if isinstance(service, InternalService):
...
Quick Reference: Why Use Disjoint Bases?
| Benefit | Impact |
|---|---|
| Architectural Rigidity | Prevents “God Class” anti-patterns |
| Static Safety | Forces bugs to appear during development |
| Code Clarity | Defines explicit domain boundaries |
| Performance | Allows type checkers to prune unreachable branches |
Why We Choose @disjoint_base
We choose the Disjoint Base pattern to enforce Domain Isolation. In microservice architectures, it is vital that your core business entities—like Customer (Internal) vs Vendor (External)—never get tangled in the same inheritance path. This pattern forces developers to think about the “nature” of their objects before they write the implementation, reducing the cognitive load and preventing future structural debt.
Developer Checklist
- Have you identified the primary, mutually exclusive domains in your system?
- Are your base foundation classes marked with
@disjoint_base? - Is your team aware that they cannot mix disjoint bases in a single subclass?
- Have you replaced complex
if/elselogic with type-hinting based on disjoint bases?
Takeaways
- Architectural Guardrails: Use
disjoint_baseto make your system’s constraints a first-class citizen of the code. - Fail Fast: Catch architectural design errors while typing, not while debugging a production incident.
- Simplicity: If a class needs two disjoint bases, your architecture has a fundamental flaw—revisit the domain model rather than fighting the type checker.