Modelo Campaign

Overview

services/api/src/models/Campaign.js define el modelo de persistencia de una campaña en MongoDB con Mongoose. El esquema guarda el nombre, el anuncio relacionado, el autor, el estado, el objetivo, el presupuesto, las métricas y notas libres. También enlaza la campaña con Listing, User y PaymentOrder mediante referencias explícitas en el esquema

[!NOTE] El esquema usa timestamps: true, así que Mongoose agrega createdAt y updatedAt automáticamente

How it works

flowchart TB
    Campaign[Campaign]
    CampaignSchema[CampaignSchema]
    BudgetSchema[BudgetSchema]
    MetricsSchema[MetricsSchema]
    Listing[Listing]
    User[User]
    PaymentOrder[PaymentOrder]

    Campaign --> CampaignSchema
    CampaignSchema --> BudgetSchema
    CampaignSchema --> MetricsSchema
    CampaignSchema --> Listing
    CampaignSchema --> User
    CampaignSchema --> PaymentOrder

Campaign se registra con mongoose.model("Campaign", CampaignSchema). CampaignSchema combina campos simples, referencias y dos subesquemas embebidos. BudgetSchema guarda presupuesto, y MetricsSchema guarda métricas agregadas. El archivo no define métodos propios; todo el comportamiento proviene de Mongoose y de la configuración del esquema

Key components

Este archivo contiene cuatro piezas principales. Cada una participa en la estructura persistida de la campaña.

{
    "title": "BudgetSchema",
    "content": "Subesquema embebido que almacena el presupuesto total, el gasto acumulado y la moneda.",
    "type": "class",
    "filePath": "services/api/src/models/Campaign.js",
    "badges": [
        "public"
    ]
}
{
    "title": "MetricsSchema",
    "content": "Subesquema embebido que guarda impresiones, clics y leads de la campa\u00f1a.",
    "type": "class",
    "filePath": "services/api/src/models/Campaign.js",
    "badges": [
        "public"
    ]
}
{
    "title": "CampaignSchema",
    "content": "Esquema principal de la campa\u00f1a. Define campos, referencias, valores por defecto y restricciones.",
    "type": "class",
    "filePath": "services/api/src/models/Campaign.js",
    "badges": [
        "public"
    ]
}
{
    "title": "Campaign",
    "content": "Modelo de Mongoose exportado por defecto. Representa la colecci\u00f3n de campa\u00f1as en la base de datos.",
    "type": "class",
    "filePath": "services/api/src/models/Campaign.js",
    "badges": [
        "public"
    ]
}

Data & fields

La estructura combina campos obligatorios, opciones con valores por defecto y subdocumentos embebidos. Las referencias conectan la campaña con otras entidades del backend

[
    {
        "name": "name",
        "type": "string",
        "required": true,
        "description": "Nombre visible de la campaña. Usa trim y no admite vacío."
    },
    {
        "name": "listingId",
        "type": "ObjectId ref Listing",
        "required": true,
        "description": "Referencia obligatoria al anuncio asociado."
    },
    {
        "name": "createdBy",
        "type": "ObjectId ref User",
        "required": true,
        "description": "Referencia obligatoria al usuario que creó la campaña."
    },
    {
        "name": "status",
        "type": "string",
        "description": "Estado de la campaña. Valores permitidos: draft, active, paused, completed, cancelled. Valor por defecto: draft."
    },
    {
        "name": "objective",
        "type": "string",
        "description": "Objetivo de la campaña. Valores permitidos: leads, visibility, calls, messages. Valor por defecto: leads."
    },
    {
        "name": "startDate",
        "type": "Date",
        "description": "Fecha de inicio. Valor por defecto: null."
    },
    {
        "name": "endDate",
        "type": "Date",
        "description": "Fecha de fin. Valor por defecto: null."
    },
    {
        "name": "budget",
        "type": "object",
        "description": "Subdocumento embebido con datos del presupuesto. Usa BudgetSchema y se inicializa con un objeto vacío.",
        "children": [
            {
                "name": "total",
                "type": "number",
                "description": "Presupuesto total. Valor por defecto: 0. No admite negativos."
            },
            {
                "name": "spent",
                "type": "number",
                "description": "Monto gastado. Valor por defecto: 0. No admite negativos."
            },
            {
                "name": "currency",
                "type": "string",
                "description": "Moneda del presupuesto. Valor por defecto: MXN. Aplica trim."
            }
        ]
    },
    {
        "name": "metrics",
        "type": "object",
        "description": "Subdocumento embebido con métricas acumuladas. Usa MetricsSchema y se inicializa con un objeto vacío.",
        "children": [
            {
                "name": "impressions",
                "type": "number",
                "description": "Impresiones acumuladas. Valor por defecto: 0. No admite negativos."
            },
            {
                "name": "clicks",
                "type": "number",
                "description": "Clics acumulados. Valor por defecto: 0. No admite negativos."
            },
            {
                "name": "leads",
                "type": "number",
                "description": "Leads acumulados. Valor por defecto: 0. No admite negativos."
            }
        ]
    },
    {
        "name": "paymentOrderId",
        "type": "ObjectId ref PaymentOrder",
        "description": "Referencia opcional a la orden de pago relacionada. Valor por defecto: null."
    },
    {
        "name": "notes",
        "type": "string",
        "description": "Notas libres de la campaña. Valor por defecto: cadena vacía. Aplica trim."
    }
]

Campos automáticos

El esquema activa timestamps: true. Eso agrega estos campos administrados por Mongoose:

  • createdAt — fecha de creación automática del documento.
  • updatedAt — fecha de última actualización automática del documento.

Behaviors

El archivo no define funciones personalizadas. Su comportamiento depende de las opciones del esquema y de Mongoose

  • new Schema(...) — construye la estructura de datos de la campaña.
  • BudgetSchema — define el subdocumento de presupuesto sin _id interno.
  • MetricsSchema — define el subdocumento de métricas sin _id interno.
  • enum en status — limita el estado a cinco valores válidos.
  • enum en objective — limita el objetivo a cuatro valores válidos.
  • min: 0 — bloquea valores negativos en presupuesto y métricas.
  • default — inicializa valores como draft, leads, MXN, 0 y null.
  • trim: true — recorta name, currency y notes antes de guardar.
  • ref — declara relaciones con Listing, User y PaymentOrder.
  • mongoose.model("Campaign", CampaignSchema) — registra el modelo para uso en el backend.

Workflows

El modelo participa en el flujo de creación y consulta de campañas. El contexto del repositorio lo conecta con el módulo de campañas y con la interfaz de backoffice

sequenceDiagram
    participant UI as CampaignsPage
    participant ROUTE as campaigns routes
    participant VALID as validateCampaignPayload
    participant MODEL as Campaign
    participant DB as MongoDB

    UI->>ROUTE: POST api campaigns
    ROUTE->>VALID: validateCampaignPayload payload
    VALID-->>ROUTE: valid or errors
    ROUTE->>MODEL: create payload
    MODEL->>DB: save campaign document
    DB-->>MODEL: stored document
    MODEL-->>ROUTE: new campaign
    ROUTE-->>UI: 201 response with data

apps/backoffice-web/src/pages/CampaignsPage.jsx envía datos de campaña. services/api/src/modules/campaigns/campaigns.routes.js valida la carga y crea el documento con este modelo. El contexto también indica que campaigns.routes.js expone lista, creación, actualización, cambio de estado y eliminación fileciteturn0file4turn0file5

Gotchas & edge cases

[!WARNING] status solo acepta draft, active, paused, completed y cancelled. Cualquier otro valor falla en la validación del esquema fileciteturn0file2turn0file10

[!WARNING] objective solo acepta leads, visibility, calls y messages. Un valor fuera de esa lista no pasa la validación fileciteturn0file2turn0file10

[!CAUTION] budget.total, budget.spent, metrics.impressions, metrics.clicks y metrics.leads no admiten negativos. El esquema bloquea esos valores con min: 0

[!NOTE] startDate y endDate aceptan null por defecto. También aceptan fechas válidas cuando se envían

[!IMPORTANT] budget y metrics son subdocumentos embebidos sin _id propio. Esa configuración está definida explícitamente en sus esquemas internos

services/api/src/modules/campaigns/campaigns.routes.js usa Campaign para operar sobre campañas. El contexto disponible muestra que también filtra por createdBy y protege la propiedad del registro fileciteturn0file4turn0file5

services/api/src/modules/campaigns/campaigns.validators.js valida campos como status, objective y budget según el contexto mostrado

apps/backoffice-web/src/pages/CampaignsPage.jsx consume este modelo desde la interfaz de backoffice para crear y consultar campañas

PaymentOrder aparece como referencia opcional mediante paymentOrderId. Eso conecta la campaña con el flujo de pago del backend