Skip to main content

11 - Document Management: Versioning, Categories and Sharing

The Document Management System of Play The Event In event management, documents represent a critical asset: invoices, contracts, permits, posters, and agreements. Discover a complete versioning system with categories, sharing, and audit trails.

What do you think about this article?

At a Glance

The Document Management System of Play The Event In event management, documents represent a critical asset: invoices, contracts, permits, posters, and agreements. Discover a complete versioning system with categories, sharing, and audit trails.

Document Management in Play The Event

Every event generates paperwork: invoices, contracts, permits, receipts, insurance policies, posters, and more. Managing these documents manually across email threads, shared drives, and local folders quickly becomes chaotic. Play The Event solves this with a structured document management system built directly into the platform, treating each document as a first-class domain entity with versioning, categorization, sharing, and metadata extraction capabilities.

This article explores the complete document management architecture: the DocumentoEvento aggregate root following Domain-Driven Design principles, the 11 predefined categories with custom category support, automatic folder detection, the full versioning system, secure sharing links, metadata extraction, file integrity verification, and the integration between documents and expenses.

What You'll Find in This Article

  • Document as an Aggregate Root following DDD patterns with optimistic locking
  • 11 predefined categories (FATTURA, SCONTRINO, CONTRATTO, etc.) explained
  • Custom categories per event with icons, colors, and ordering
  • Automatic folder detection based on MIME type and document category
  • Full versioning system: add, restore, and browse version history
  • Sharing links with expiration, download limits, and revocation
  • Metadata extraction from EXIF, PDF properties, and Office documents
  • File integrity verification with SHA-256 hashing and 50MB size limits
  • Document-to-expense linking for receipt and invoice management
  • Organized storage with separate directories by file type

Document as an Aggregate Root

In Play The Event, the DocumentoEvento class is designed as an Aggregate Root following Domain-Driven Design (DDD) principles. This means the document entity is the single entry point for all operations related to document management, versions, and sharing links. No external code manipulates versions or links directly; everything goes through the aggregate root, which enforces all business invariants.

DocumentoEvento - Aggregate Root Structure
AGGREGATE ROOT: DocumentoEvento
│
├── Identity
│   ├── id (Long, auto-generated)
│   ├── eventoId (Long, event reference)
│   └── versione (Long, optimistic locking)
│
├── Content Metadata
│   ├── titolo (String, max 200 chars)
│   ├── descrizione (String, max 1000 chars)
│   ├── nomeFileOriginale (String, original filename)
│   ├── mimeType (String, content type)
│   └── dimensioneBytes (Long, max 50MB)
│
├── Classification
│   ├── categoria (CategoriaDocumento enum)
│   ├── categoriaPersonalizzataId (Long, optional)
│   └── tipoCartella (TipoCartella enum, auto-detected)
│
├── Ownership & Tracking
│   ├── caricatoDaId (Long, uploader user ID)
│   ├── spesaId (Long, linked expense)
│   ├── creatoIl (Instant, creation timestamp)
│   └── aggiornatoIl (Instant, last modified)
│
├── Extracted Metadata (JSON)
│   └── metadati (Map<String, Object>)
│
├── Child Entities: Versions
│   └── versioni (List<VersioneDocumento>)
│       ├── Version 1 (initial upload)
│       ├── Version 2 (updated file)
│       └── Version N...
│
└── Child Entities: Sharing Links
    └── linkCondivisione (Set<LinkCondivisioneDocumento>)
        ├── Link A (active, 5 days remaining)
        ├── Link B (expired)
        └── Link C (revoked)

The aggregate root uses optimistic locking via the @Version annotation. If two users attempt to modify the same document concurrently, the second write will fail with an OptimisticLockException, preventing data corruption without requiring database-level pessimistic locks.

Factory Method - Creating a New Document
public static DocumentoEvento crea(
        Long eventoId,
        String titolo,
        String descrizione,
        CategoriaDocumento categoria,
        Long categoriaPersonalizzataId,
        String nomeFileOriginale,
        String mimeType,
        Long dimensioneBytes,
        Long caricatoDaId,
        String percorsoStorage,
        String hashContenuto
) {
    // Validate all creation parameters
    validaParametriCreazione(eventoId, titolo,
        nomeFileOriginale, mimeType,
        dimensioneBytes, caricatoDaId, percorsoStorage);

    DocumentoEvento doc = new DocumentoEvento();
    doc.eventoId = eventoId;
    doc.titolo = titolo.trim();
    doc.categoria = categoria != null
        ? categoria : CategoriaDocumento.ALTRO;
    doc.tipoCartella = TipoCartella
        .fromMimeTypeAndCategoria(mimeType, doc.categoria);
    doc.versioneCorrente = 1;

    // Create the first version automatically
    VersioneDocumento primaVersione = VersioneDocumento.crea(
        1, nomeFileOriginale, percorsoStorage,
        dimensioneBytes, mimeType, hashContenuto,
        caricatoDaId, "Versione iniziale"
    );
    primaVersione.setDocumento(doc);
    doc.versioni.add(primaVersione);

    return doc;
}

Notice that the factory method is static and the constructor is protected. This pattern ensures that every DocumentoEvento instance is created through the controlled factory method, where all invariants are validated before the object enters the system. The first version is created automatically during document creation with the note "Versione iniziale" (initial version).

11 Predefined Categories

Play The Event provides 11 predefined document categories that cover the most common types of paperwork in event management. Each category carries a display name and an icon identifier used in the UI.

CategoriaDocumento Enum

Enum Value English Meaning Icon Use Case
FATTURA Invoice file-text Vendor invoices, service billing
SCONTRINO Receipt receipt Purchase receipts, expense proofs
PERMESSO Permit shield-check Municipal permits, venue authorizations
BANDO Public Notice / Call megaphone Calls for proposals, public tenders
CONTRATTO Contract file-signature Vendor contracts, venue agreements
PREVENTIVO Quote / Estimate calculator Cost estimates, price quotes
ASSICURAZIONE Insurance shield Event liability, equipment insurance
CERTIFICATO Certificate award Safety certifications, compliance docs
LOCANDINA Poster / Flyer image Event posters, promotional materials
PERSONALIZZATA Custom Category tag Event-specific custom categories
ALTRO Other file Default fallback for uncategorized files

The enum includes built-in intelligence. For example, the isRicevuta() method returns true for both FATTURA and SCONTRINO, allowing the system to route receipts and invoices to dedicated storage folders automatically. The fromMimeType() method provides default categorization based on content type: image files default to LOCANDINA, everything else defaults to ALTRO.

Category Intelligence
public boolean isRicevuta() {
    return this == FATTURA || this == SCONTRINO;
}

public static CategoriaDocumento fromMimeType(String mimeType) {
    if (mimeType == null) {
        return ALTRO;
    }
    if (mimeType.startsWith("image/")) {
        return LOCANDINA;
    }
    return ALTRO;
}

Custom Categories per Event

While the 11 predefined categories cover most scenarios, every event is unique. A music festival might need a "Stage Plan" category, while a corporate conference might need "Speaker Agreement." The CategoriaDocumentoPersonalizzata entity allows organizers to define custom document categories scoped to their specific event.

Custom Category Entity
CategoriaDocumentoPersonalizzata
├── id (Long)
├── eventoId (Long) ─── scoped to this event
├── nome (String, max 50 chars) ─── unique per event
├── descrizione (String, max 200 chars)
├── icona (String, default: "file-text")
├── colore (String, hex format #RRGGBB, default: #6b7280)
├── ordine (Integer) ─── display ordering
└── creatoIl (Instant)

UNIQUENESS CONSTRAINT:
  (evento_id, nome) must be unique
  → No duplicate category names within the same event

VALIDATION RULES:
  ├── nome: required, max 50 characters
  ├── descrizione: optional, max 200 characters
  ├── colore: hex format (#RRGGBB) or default #6b7280
  └── icona: any icon name or default "file-text"

Custom categories are linked to documents via the categoriaPersonalizzataId field on DocumentoEvento. When a document's category is set to PERSONALIZZATA, the custom category ID is stored; for any other category, the custom ID is automatically cleared to null, maintaining referential consistency.

Custom Category Features

  • Color-coded: Each category has a hex color for visual distinction in the UI
  • Icon support: Custom icon from the Lucide icon set
  • Ordered display: The ordine field controls category ordering
  • Event-scoped: Categories are isolated per event, no cross-event leakage
  • Validated: Color must be valid hex (#RRGGBB), name must be unique per event

Automatic Folder Detection

When a document is uploaded to Play The Event, the system automatically determines the appropriate storage folder based on two factors: the file's MIME type and the document's category. This is handled by the TipoCartella enum.

TipoCartella - Automatic Folder Assignment
FOLDER DETECTION ALGORITHM

Input: mimeType (String), categoria (CategoriaDocumento)

Step 1: Check if category is a receipt type
  IF categoria == FATTURA or categoria == SCONTRINO
  THEN → RICEVUTE folder ("/ricevute")

Step 2: Check if file is an image
  IF mimeType starts with "image/"
  THEN → IMMAGINI folder ("/immagini")

Step 3: Default
  ELSE → DOCUMENTI folder ("/documenti")

EXAMPLES:
  ┌──────────────────────────┬──────────────┬──────────────┐
  │ File                     │ Category     │ Folder       │
  ├──────────────────────────┼──────────────┼──────────────┤
  │ invoice_2026.pdf         │ FATTURA      │ /ricevute    │
  │ receipt_001.jpg          │ SCONTRINO    │ /ricevute    │
  │ event_poster.png         │ LOCANDINA    │ /immagini    │
  │ team_photo.jpg           │ ALTRO        │ /immagini    │
  │ venue_contract.pdf       │ CONTRATTO    │ /documenti   │
  │ safety_cert.docx         │ CERTIFICATO  │ /documenti   │
  │ budget_estimate.xlsx     │ PREVENTIVO   │ /documenti   │
  └──────────────────────────┴──────────────┴──────────────┘

The priority is important: category takes precedence over MIME type. If an organizer uploads a JPEG photograph of a receipt and categorizes it as SCONTRINO, the file goes to the /ricevute folder, not the /immagini folder. This ensures that financial documents stay grouped together regardless of their format, which is critical for audit trails and expense reconciliation.

Three Storage Folders

  • DOCUMENTI - Contracts, permits, estimates, certificates, and general files
  • IMMAGINI - Photos, posters, flyers, and visual assets
  • RICEVUTE - Invoices and receipts, isolated for financial tracking

Detection Priority

  • 1st: Category check (FATTURA/SCONTRINO override everything)
  • 2nd: MIME type check (image/* goes to IMMAGINI)
  • 3rd: Default (everything else to DOCUMENTI)

Versioning System

Documents evolve over time. A contract might go through multiple revisions, an event poster might need design updates, and a budget estimate might change as vendors respond with quotes. Play The Event supports full document versioning, allowing users to upload new versions, browse the complete history, and restore any previous version.

VersioneDocumento Entity
VersioneDocumento
├── id (Long)
├── documentoId (Long) ─── parent document
├── numeroVersione (Integer) ─── sequential, starting at 1
├── nomeFileOriginale (String, max 255 chars)
├── percorsoStorage (String, max 500 chars)
├── dimensioneBytes (Long)
├── mimeType (String, max 100 chars)
├── hashContenuto (String, SHA-256, 64 chars)
├── creatoDaId (Long) ─── who uploaded this version
├── notaVersione (String, max 500 chars) ─── change note
└── creatoIl (Instant, creation timestamp)

CONSTRAINTS:
  UNIQUE(documento_id, numero_versione)
  → No duplicate version numbers per document

INDEXES:
  (documento_id) ─── fast lookup by document
  (documento_id, numero_versione DESC) ─── ordered history

Adding a New Version

When a user uploads a new version of an existing document, the aggregate root increments the version counter, updates the top-level file metadata, and creates a new VersioneDocumento entity in a single atomic operation.

Version Addition Flow
public VersioneDocumento aggiungiVersione(
        String nomeFileOriginale,
        String percorsoStorage,
        Long dimensioneBytes,
        String mimeType,
        String hashContenuto,
        Long caricatoDaId,
        String notaVersione
) {
    // Validate all version parameters
    validaParametriVersione(nomeFileOriginale,
        percorsoStorage, dimensioneBytes,
        mimeType, caricatoDaId);

    // Increment version counter
    this.versioneCorrente++;

    // Update document-level metadata
    this.nomeFileOriginale = nomeFileOriginale;
    this.mimeType = mimeType;
    this.dimensioneBytes = dimensioneBytes;

    // Create the new version entity
    VersioneDocumento nuovaVersione = VersioneDocumento.crea(
        this.versioneCorrente,
        nomeFileOriginale, percorsoStorage,
        dimensioneBytes, mimeType, hashContenuto,
        caricatoDaId, notaVersione
    );
    nuovaVersione.setDocumento(this);
    this.versioni.add(nuovaVersione);

    return nuovaVersione;
}

Restoring a Previous Version

Restoring a version does not overwrite history. Instead, it creates a new version with the content of the previous one, maintaining a complete audit trail. If you restore version 2 when the current version is 5, the system creates version 6 with the content of version 2 and the note "Ripristino versione 2".

Version Restore Flow
VERSION RESTORE EXAMPLE

Document: "Venue Contract.pdf"
Current: Version 5

User action: Restore Version 2

BEFORE RESTORE:
  v1 ─ Initial upload (Jan 10)
  v2 ─ Added appendix A (Jan 15)
  v3 ─ Price update (Jan 20)
  v4 ─ Legal review changes (Feb 1)
  v5 ─ Final signatures (Feb 5) ← current

AFTER RESTORE:
  v1 ─ Initial upload (Jan 10)
  v2 ─ Added appendix A (Jan 15)
  v3 ─ Price update (Jan 20)
  v4 ─ Legal review changes (Feb 1)
  v5 ─ Final signatures (Feb 5)
  v6 ─ "Ripristino versione 2" (Feb 14) ← current
       (same content as v2)

KEY POINT: No history is lost.
Version 5 remains accessible.

Immutable Version History

Versions are never deleted or overwritten. The restore operation creates a new version pointing to the same storage path and hash as the source version. This design ensures a complete, tamper-evident audit trail that satisfies regulatory requirements for document management in event planning, especially when public funding or permits are involved.

Sharing Links

Organizers often need to share documents with external stakeholders: venue managers, city officials, sponsors, or vendors who do not have an account on Play The Event. The platform provides temporary sharing links that allow unauthenticated access to specific documents with built-in security controls.

LinkCondivisioneDocumento Entity
LinkCondivisioneDocumento
├── id (Long)
├── documentoId (Long) ─── linked document
├── token (String, 64 chars, UUID-based, unique)
├── scadenza (Instant) ─── expiration timestamp
├── creatoDaId (Long) ─── who created the link
├── numeroDownload (Integer, starts at 0)
├── maxDownload (Integer, null = unlimited)
├── revocato (Boolean, default false)
├── revocatoIl (Instant, revocation timestamp)
└── creatoIl (Instant, creation timestamp)

SECURITY CONSTRAINTS:
  Maximum link duration: 30 days
  Token: 64 hex characters (UUID-based, unique index)
  Validation: expiration + download count + revocation

Link Validity Check

A sharing link is considered valid only when all three conditions are met simultaneously. If any one fails, the link is invalid and the download is blocked.

Three-Way Validity Check
LINK VALIDITY = ALL of:

  1. NOT revoked
     ├── revocato == false
     └── If true → link was manually disabled

  2. NOT expired
     ├── Instant.now() is BEFORE scadenza
     └── If past → time-based expiration

  3. Downloads NOT exhausted
     ├── maxDownload == null → unlimited ✓
     └── numeroDownload < maxDownload → still valid ✓

EXAMPLE SCENARIOS:
  ┌─────────┬─────────┬──────────┬──────────┐
  │ Revoked │ Expired │ Downloads│ Valid?   │
  ├─────────┼─────────┼──────────┼──────────┤
  │ false   │ false   │ 2/5      │ YES ✓    │
  │ false   │ false   │ 5/5      │ NO ✗     │
  │ false   │ true    │ 0/∞      │ NO ✗     │
  │ true    │ false   │ 0/5      │ NO ✗     │
  │ false   │ false   │ 3/∞      │ YES ✓    │
  └─────────┴─────────┴──────────┴──────────┘

Link Creation

  • Generated through the aggregate root only
  • Token: 64-character hex from two UUIDs
  • Duration validated (positive, max 30 days)
  • Max download count optional (null = unlimited)
  • Tracks creation time and creator ID

Link Revocation

  • Immediate: sets revocato = true
  • Records revocation timestamp
  • Idempotent: revoking twice is safe
  • Active links filterable via getLinkAttivi()
  • Revoked links remain in history for audit

Download Tracking

Every time a sharing link is used to download a document, the registraDownload() method increments the download counter. This operation first checks link validity; if the link is no longer valid (expired, revoked, or downloads exhausted), an IllegalStateException is thrown, preventing the download.

Download Registration
public void registraDownload() {
    if (!isValido()) {
        throw new IllegalStateException(
            "Cannot register download: link not valid"
        );
    }
    this.numeroDownload++;
}

// Helper methods for remaining resources
public Duration getTempoRimanente() {
    if (isScaduto()) return Duration.ZERO;
    return Duration.between(Instant.now(), scadenza);
}

public Integer getDownloadRimanenti() {
    if (maxDownload == null) return null; // unlimited
    return Math.max(0, maxDownload - numeroDownload);
}

Metadata Extraction

When a file is uploaded, Play The Event extracts available metadata and stores it as a JSON map in the metadati field. This provides valuable context about each document without requiring users to manually enter information.

Metadata Storage and Extraction
METADATA EXTRACTION BY FILE TYPE

IMAGE FILES (JPEG, TIFF, PNG):
  ├── EXIF data
  │   ├── camera make/model
  │   ├── date taken
  │   ├── GPS coordinates (if available)
  │   ├── resolution (width x height)
  │   └── orientation
  ├── IPTC data
  │   ├── caption/description
  │   ├── keywords
  │   └── copyright
  └── XMP data
      ├── creator tool
      └── custom metadata

PDF FILES:
  ├── title
  ├── author
  ├── subject
  ├── creator application
  ├── creation date
  ├── modification date
  ├── page count
  └── PDF version

OFFICE FILES (DOCX, XLSX, PPTX):
  ├── title
  ├── author
  ├── last modified by
  ├── creation date
  ├── modification date
  ├── word count (documents)
  ├── slide count (presentations)
  └── sheet names (spreadsheets)

STORAGE FORMAT:
  Column: metadati (JSON)
  Converter: JsonMapConverter
  Type: Map<String, Object>

The metadata system is designed to be flexible. The Map<String, Object> type allows storing any key-value pair, accommodating different metadata schemas from different file types. Individual metadata entries can be added or queried through dedicated methods on the aggregate root.

Metadata Operations
// Set all metadata at once (after extraction)
doc.impostaMetadati(extractedMetadata);

// Add or update a single metadata entry
doc.aggiungiMetadato("pageCount", 15);
doc.aggiungiMetadato("author", "Federico Calo");

// Query specific metadata
Object pageCount = doc.getMetadato("pageCount");

// Check if metadata exists
boolean hasMetadata = doc.haMetadati();

File Integrity: SHA-256 Hashing

Every file version in Play The Event stores a SHA-256 hash of the file content in the hashContenuto field. This 64-character hexadecimal string serves as a cryptographic fingerprint, enabling integrity verification at any point in the document's lifecycle.

Integrity and Size Validation
FILE INTEGRITY SYSTEM

SHA-256 HASH:
  ├── Generated at upload time
  ├── Stored per version (not per document)
  ├── 64-character hex string
  ├── Example: "a1b2c3d4e5f6...7890abcdef"
  └── Used for:
      ├── Deduplication detection
      ├── Corruption verification
      ├── Tamper detection
      └── Version comparison

SIZE LIMITS:
  Maximum file size: 50MB (52,428,800 bytes)
  Validated at:
  ├── Document creation (factory method)
  └── Version addition (each new upload)

  private static final long MAX_DIMENSIONE_BYTES =
      50L * 1024 * 1024; // 50MB

VALIDATION:
  if (dimensioneBytes > MAX_DIMENSIONE_BYTES) {
      throw new IllegalArgumentException(
          "File troppo grande (max 50MB)"
      );
  }

Why SHA-256 per Version?

The hash is stored on each VersioneDocumento rather than on the parent DocumentoEvento. This allows the system to verify integrity of every individual version independently. When restoring a previous version, the hash from the original version is carried over, enabling verification that the restored content matches the original exactly.

Document-Expense Linking

One of the most powerful features of the document system is its integration with the expense tracking module. Receipts and invoices can be linked directly to expense entries, creating a complete financial audit trail.

Expense Linking Operations
DOCUMENT-EXPENSE LINKING

Link a document to an expense:
  doc.collegaASpesa(spesaId);
  → Sets spesaId on the document
  → Enables bi-directional query

Unlink a document from an expense:
  doc.scollegaDaSpesa();
  → Sets spesaId to null

Query:
  doc.isCollegatoASpesa();
  → Returns true if spesaId != null

DATABASE INDEX:
  idx_documento_spesa ON (spesa_id)
  → Fast lookup of all documents for an expense

USE CASE FLOW:
  1. Organizer creates expense "Venue Deposit - $2000"
  2. Uploads receipt photo (JPEG)
     → Category: SCONTRINO
     → Folder: /ricevute (auto-detected)
  3. Links document to expense
     → doc.collegaASpesa(expenseId)
  4. Later, accountant queries all receipts for expense
     → SELECT * FROM documenti_evento
        WHERE spesa_id = ?

Linked Documents Benefit

  • Complete audit trail from expense to receipt
  • Financial reporting with attached proofs
  • Budget reconciliation with document verification
  • Compliance with grant and subsidy requirements

Query Helpers

  • isCollegatoASpesa() - checks link status
  • isRicevuta() - checks if receipt folder
  • isImmagine() - checks if image folder
  • haCategoriaPersonalizzata() - custom category check

Organized Storage Architecture

The document management system in Play The Event organizes files into a clear directory structure that combines event isolation with automatic categorization.

Storage Directory Structure
STORAGE LAYOUT

/events
└── /{eventoId}
    └── /documents
        ├── /documenti      ← contracts, permits, certificates
        │   ├── doc_001_v1.pdf
        │   ├── doc_001_v2.pdf
        │   ├── doc_005_v1.docx
        │   └── ...
        │
        ├── /immagini       ← photos, posters, visual assets
        │   ├── doc_002_v1.jpg
        │   ├── doc_003_v1.png
        │   └── ...
        │
        └── /ricevute       ← invoices and receipts
            ├── doc_004_v1.pdf
            ├── doc_004_v2.pdf
            └── ...

KEY PROPERTIES:
  ├── Event isolation: each event has its own directory tree
  ├── Folder auto-detection: based on MIME + category
  ├── Version co-location: all versions of a document
  │   live in the same folder
  ├── Path stored per version: enables independent access
  └── Path updateable: supports post-creation path adjustment

Each version stores its own percorsoStorage (storage path), with a maximum length of 500 characters. The aggiornaPercorsoStorage() method on VersioneDocumento allows updating the path after initial creation, which is essential during the save flow when the document ID is not yet available at object construction time.

Database Indexing Strategy

Performance is a key consideration for the document system. The domain model declares strategic database indexes to ensure fast queries across the most common access patterns.

Index Map

Index Columns Purpose
idx_documento_evento evento_id List all documents for an event
idx_documento_categoria categoria Filter documents by category
idx_documento_tipo_cartella tipo_cartella Filter by storage folder type
idx_documento_caricato_da caricato_da_id Find documents uploaded by a user
idx_documento_spesa spesa_id Find receipts linked to an expense
idx_versione_documento documento_id List all versions of a document
idx_versione_numero documento_id, numero_versione DESC Ordered version history
idx_link_token token (unique) Fast sharing link lookup
idx_link_documento documento_id All sharing links for a document
idx_link_scadenza scadenza Cleanup expired links

In the Next Article

With document management covered, the next article will explore another critical aspect of the platform's domain model. The document system works in concert with expense tracking, event management, and participant coordination to deliver a comprehensive event planning experience. Visit www.playtheevent.com to see how all these systems work together in production.

Key Takeaways

  • Documents are modeled as Aggregate Roots with optimistic locking for concurrent access safety
  • 11 predefined categories cover common event document types, with custom categories for flexibility
  • Automatic folder detection routes files to DOCUMENTI, IMMAGINI, or RICEVUTE based on MIME type and category
  • Full versioning preserves complete history; restoring a version creates a new entry, never overwrites
  • Sharing links provide controlled external access with expiration, download limits, and revocation
  • SHA-256 hashing per version enables integrity verification and tamper detection
  • Document-expense linking creates a complete financial audit trail from expense to receipt
  • Metadata extraction captures EXIF, PDF, and Office properties automatically on upload
  • Strategic database indexes optimize the most common query patterns across all entities

Frequently asked questions

What is the purpose of the aggregate root in Play The Event's document management system?

The aggregate root serves as a single entry point for all operations related to document management, versions, and sharing links, enforcing business invariants and preventing data corruption.

How does Play The Event handle custom categories for documents?

Custom categories are defined by event organizers using the entity, allowing them to create unique categories scoped to their specific event, which can be linked to documents via the field on .

What is the purpose of automatic folder detection in Play The Event's document management system?

Automatic folder detection determines the appropriate storage folder for a document based on its category and MIME type, ensuring that financial documents stay grouped together regardless of their format.

How does Play The Event handle versioning and restoring previous versions of documents?

Play The Event supports full document versioning, allowing users to upload new versions, browse the complete history, and restore any previous version without overwriting or deleting existing versions.

Commenti

Caricamento commenti...

Sign in to comment

Discuss this article in the community

Join the Telegram group to discuss with other developers, ask questions and share your experiences.

Did you like this article?

Explore other content on the blog or discover my projects