Skip to content

GTM Domain

The gtm_domain bounded context models sales opportunities and pipeline risk. It surfaces revenue-at-risk signals when a customer with an open expansion opportunity is also flagged as high churn risk.

Entities

src.domain.gtm.entities

Opportunity entity for the GTM bounded context.

Classes

Opportunity dataclass

A sales or expansion opportunity linked to a customer.

Parameters:

Name Type Description Default
opp_id str

UUID primary key.

required
customer_id str

FK to Customer entity.

required
stage SalesStage

Current CRM pipeline stage.

required
close_date date

Actual or expected close date.

required
amount Decimal

USD opportunity value.

required
sales_owner str

Anonymised sales rep identifier.

required
Source code in src/domain/gtm/entities.py
@dataclass
class Opportunity:
    """A sales or expansion opportunity linked to a customer.

    Args:
        opp_id: UUID primary key.
        customer_id: FK to Customer entity.
        stage: Current CRM pipeline stage.
        close_date: Actual or expected close date.
        amount: USD opportunity value.
        sales_owner: Anonymised sales rep identifier.
    """

    opp_id: str
    customer_id: str
    stage: SalesStage
    close_date: date
    amount: Decimal
    sales_owner: str

    @property
    def is_at_risk(self) -> bool:
        """True if an open opportunity exists for a customer who may churn.

        Used in the GTM domain to flag revenue at risk in the sales pipeline.
        """
        return self.stage.is_open and self.close_date >= date.today()
Attributes
is_at_risk property
is_at_risk: bool

True if an open opportunity exists for a customer who may churn.

Used in the GTM domain to flag revenue at risk in the sales pipeline.

Value Objects

src.domain.gtm.value_objects

Value objects for the GTM domain.

Classes

SalesStage

Bases: StrEnum

CRM pipeline stage for an opportunity.

Source code in src/domain/gtm/value_objects.py
class SalesStage(StrEnum):
    """CRM pipeline stage for an opportunity."""

    PROSPECTING = "prospecting"
    QUALIFICATION = "qualification"
    PROPOSAL = "proposal"
    CLOSED_WON = "closed_won"
    CLOSED_LOST = "closed_lost"

    @property
    def is_open(self) -> bool:
        return self not in {SalesStage.CLOSED_WON, SalesStage.CLOSED_LOST}

Repository Interface

src.domain.gtm.repository

OpportunityRepository – abstract port for the GTM domain.

Classes

OpportunityRepository

Bases: ABC

Port for persisting and retrieving Opportunity entities.

Source code in src/domain/gtm/repository.py
class OpportunityRepository(ABC):
    """Port for persisting and retrieving Opportunity entities."""

    @abstractmethod
    def get_open_for_customer(self, customer_id: str) -> Sequence[Opportunity]:
        """Return all open opportunities for a given customer."""
        ...
Functions
get_open_for_customer abstractmethod
get_open_for_customer(customer_id: str) -> Sequence[Opportunity]

Return all open opportunities for a given customer.

Source code in src/domain/gtm/repository.py
@abstractmethod
def get_open_for_customer(self, customer_id: str) -> Sequence[Opportunity]:
    """Return all open opportunities for a given customer."""
    ...