One of the most important domain objects in an event-planning application is the Guest.
At first glance, it would be easy to think of a Guest as little more than a name in a list.
That would be far too simplistic.
A Guest is not merely somebody who exists in an address book. A Guest is somebody participating in a specific event, and that event context changes the kind of information the application needs to understand.
The model needs to know who the person is, how their name should be displayed, how they can be contacted, whether they have responded to the invitation, what meal they require, whether their seating is fixed, whether they belong to a Group, whether they are associated with a Table, whether they have special requirements, and whether the organiser needs additional event-specific information that was not anticipated when the software was designed.
A Guest builds on the common Event Object
The Guest does not need to reinvent concepts that are shared by many objects in the Event Document.
It participates in the common Event Object model, which already provides the shared concepts of stable object identity and notes.
The Guest then adds the information that is specific to an event participant.
COMMON EVENT OBJECT
│
├── Object Identifier
├── Notes
└── Display-name capability
│
▼
GUEST
│
├── Personal naming
├── Event classifications
├── Contact Information
├── Group association
├── Table association
├── Custom values
└── Optional image
This separation is useful because the Guest does not need to know how identifiers are generated or how generic object notes are managed.
Its responsibility is to represent the participant.
The Guest has several distinct categories of information
The Guest model becomes much easier to understand if the data is divided by purpose rather than treated as one large collection of unrelated fields.
GUEST │ ├── Identity and common metadata │ ├── Personal naming │ ├── Event classifications │ ├── Contact Information │ ├── Relationships │ ├── Extensible custom information │ └── Optional visual representation
This is the structure I find most useful for thinking about the class.
Identity and common metadata
Every Guest has a stable Object Identifier inherited from the common Event Object layer.
This is important because the Guest's identity must remain independent from the Guest's name.
A Guest may change their family name.
The organiser may change the display name.
A title may be corrected.
None of those changes should turn the Guest into a different object.
The same common layer also provides notes.
Notes are descriptive information associated with the Guest, but they are not part of the Guest's identity.
Personal naming is more complex than one string
The Guest does not rely on one simple “name” field.
It carries several components that together allow the application to present a person properly.
The stored naming information includes:
- anonymous status;
- display name;
- title;
- first name;
- family name;
- suffix.
This is an important design choice.
A single string would be easier, but it would make sorting, formatting, filtering and internationalisation much more difficult.
Anonymous status is a real domain state
The Guest can explicitly be marked as anonymous.
This is not the same as simply having an empty name.
An empty name could mean incomplete data.
Anonymous status means that the lack of a conventional displayed identity is intentional.
This allows lists, reports and Group-related views to treat anonymous Guests consistently.
Display name and structured name are separate
The Guest stores both structured name components and a display name.
This is useful because the way a person should appear in the application may not always be identical to a mechanically generated combination of title, first name, family name and suffix.
STRUCTURED NAME
Title
First Name
Family Name
Suffix
│
▼
Formatting rules
│
▼
Formatted Guest name
OPTIONAL DISPLAY NAME
May provide the preferred
presentation form directly
This allows the model to preserve both semantic structure and practical presentation.
Title
The Guest contains a title value.
Conceptually this can represent values such as professional or social titles.
The model also contains functionality for working with titles in sorting and presentation.
The important point is not the exact list of titles, but that title is treated as part of personal naming rather than being embedded into a single opaque name string.
First name
The Guest stores the given or first name separately.
This supports formatting, sorting, searching and the creation of shortened or familiar representations.
Family name
The family or last name is also stored independently.
This is especially useful when Guest lists need to be sorted by family name while the normal display remains first-name-first.
Suffix
A suffix can also form part of the Guest's structured name.
The model includes supporting behaviour for treating suffixes consistently in display and sorting.
Naming behaviour should remain inside the domain model
The Guest needs to provide several functional naming operations.
These include the ability to:
- produce the normal formatted display name;
- produce a presentation-safe representation where needed;
- combine the available name components consistently;
- derive initials;
- provide title-aware sorting behaviour;
- provide suffix-aware sorting behaviour.
This is important because the application may display the same Guest in many places.
Guest │ ├── Guest List ├── Group View ├── Table View ├── Floor Plan ├── Report └── Export
If every view constructs the Guest's name independently, inconsistencies are almost guaranteed.
The Guest should therefore own the semantic naming rules.
Meal preference
The Guest stores a meal preference.
This is clearly event-specific information.
It does not belong in the Contact Information value object because it describes the person's participation in the event, not their general contact identity.
The functional responsibilities include retrieving and updating the selected meal information and participating in the application's meal-selection workflow.
Gender category
The Guest includes a gender classification.
This is represented as a defined category rather than arbitrary free text.
The class therefore needs functional behaviour for:
- retrieving the current category;
- changing it;
- converting the category into a display form;
- interpreting suitable external or presentation values where required.
The important architectural point is that the rest of the application should not scatter its own string comparisons for these states.
Age category
Age is also represented as an event-oriented classification rather than necessarily storing an exact date of birth.
This is a useful example of modelling only what the application actually needs.
An event may care whether somebody falls into an adult or child category for catering, pricing or seating without needing to collect their full birth date.
RSVP status
The Guest contains an explicit RSVP state.
This is more appropriate than a simple boolean because invitation response is rarely limited to only two meaningful states.
RSVP
Not yet resolved
│
├── accepted
├── declined
└── other application-defined state
The Guest needs functional operations for retrieving, changing and presenting the RSVP state consistently.
VIP status
The Guest may be explicitly marked as a VIP.
This is another simple stored state with potentially significant consequences elsewhere.
The Guest itself only needs to know whether the VIP flag is set.
Other layers can interpret that fact.
Guest VIP = Yes │ ├── UI may show an indicator ├── Reports may prioritise the Guest ├── Filters may isolate VIP Guests └── Seating rules may use the classification
This separation keeps business interpretation outside the raw stored state.
Seating lock
The Guest can also be marked as locked for seating purposes.
This has a very clear functional meaning:
the Guest's current placement should be preserved when automatic or semi-automatic seating operations are performed.
Guest assigned to Table
│
▼
Seating locked?
│ │
No Yes
│ │
▼ ▼
May move Preserve placement
during during automatic
optimisation seating operations
This is a good example of a value stored by the Guest but interpreted by higher-level business rules.
Special requirements
The Guest stores special requirements as part of the event context.
This may contain information the organiser needs when planning the Guest's participation.
Examples could include accessibility or operational requirements, although the application should avoid collecting unnecessary sensitive detail.
There is an important distinction here.
Human-readable requirement
│
└── Special Requirements
Requirement needed by automation
│
└── Structured constraint
If the automatic seating system eventually needs to understand that a Guest requires a wheelchair-accessible seating position, a purely free-form note may not be enough.
That requirement may need to become structured data elsewhere in the domain model.
Individual Contact Information
The Guest contains an embedded Contact Information value object.
That value stores:
- work phone;
- home phone;
- mobile phone;
- email address.
GUEST
│
└── Contact Information
│
├── Work phone
├── Home phone
├── Mobile phone
└── Email
This is a value object rather than a separate independent business entity.
The Contact Information belongs directly to the Guest's state.
Contact behaviour can also use Group information
A particularly useful aspect of the model is that the Guest can answer higher-level questions such as whether usable email or telephone information is available.
That question can involve the Guest's Group as well as the Guest's own Contact Information.
Need contact information
│
▼
Guest has it?
│ │
Yes No
│ │
▼ ▼
use it Group has shared
contact information?
│ │
Yes No
│ │
▼ ▼
use it none
This is useful for families, couples and other shared-contact situations.
More importantly, it prevents each caller from implementing its own fallback rules.
Group association
A Guest may belong to a Group.
This is an association rather than embedded Group data.
GROUP │ ├── Guest A ├── Guest B └── Guest C Guest A │ └── associated Group ──► Group
The relationship is therefore navigable from both directions at the domain level.
The Group keeps references to its Guests, and the individual Guest can identify its Group.
This does not mean that the Guest owns the Group or that the Group owns the lifetime of the Guest.
It is a domain relationship.
Functional Group operations
The Guest should functionally support:
- determining whether a Group is associated;
- retrieving the associated Group;
- associating or clearing a Group;
- obtaining relevant Group naming information;
- participating in Group-related contact behaviour.
These operations describe what the Guest needs to accomplish without making the public model dependent on implementation-level method names.
Current Table association
The current model also associates a Guest directly with a Table.
Guest │ └── associated Table ──► Table
This is worth stating precisely because it differs from one of the architectural directions I have been considering for the future.
A richer seating model could eventually make the relationship:
Guest │ ▼ Chair / Seat Position │ ▼ Table
But that is a design direction, not the present Guest relationship.
The current Guest model directly stores the Table association.
Why Table and Group are not construction parameters
An interesting detail is that the Guest's main creation operation establishes the Guest's intrinsic state without requiring Group or Table relationships at the same time.
That suggests a useful lifecycle.
CREATE GUEST
│
├── identity
├── names
├── meal
├── classifications
├── RSVP
├── VIP
├── requirements
├── contact information
└── custom values
│
▼
Guest exists
│
├── associate Group
└── associate Table
This is particularly useful during document loading.
The application can create all objects first and resolve relationships afterwards once all referenced Groups and Tables exist.
Optional Guest image
The Guest can reference an optional image resource.
This can support a portrait or another visual representation of the person.
Guest
│
└── optional image reference
│
▼
shared image resource
The image should be understood as a referenced resource rather than assuming that the Guest owns the image's lifetime independently.
Custom Guest values
One of the more important extensibility features is the collection of custom values.
No event-planning application can predict every field every organiser will ever want.
One organiser may need hotel information.
Another may need badge text.
Another may need a company department.
Another may need transport information.
Another may need an internal code.
It would be poor architecture to permanently expand the Guest class every time one new business requirement appears.
GUEST Standard model │ ├── Name ├── Meal ├── RSVP ├── Contact ├── VIP └── ... Extensible values │ ├── Hotel ├── Badge Name ├── Department ├── Flight ├── Transport └── user-defined fields
The Guest therefore stores generic custom values associated with separately defined custom Guest fields.
Functional custom-field operations
The Guest needs to support:
- retrieving a custom value;
- replacing a custom value;
- adding another value;
- removing a value;
- determining how many custom values currently exist.
The definitions of those fields belong elsewhere.
The Guest stores the values.
Affinity belongs outside the Guest
The Guest participates in affinity-related presentation and analysis, but the Guest itself does not store the complete affinity relationship collection.
This is an important distinction.
An affinity is inherently a relationship between people.
Guest A │ │ affinity relationship ▼ Guest B
That relationship belongs more naturally to a broader event-level relationship model than to one Guest exclusively.
The Guest can still provide presentation information associated with affinity state without owning the underlying graph.
The Guest is not a user-interface object
The Guest should remain independent from the screens used to edit or display it.
That means it should not know:
- which dialog edits it;
- which list displays it;
- which colour represents RSVP;
- where a button is located;
- how a Table is drawn;
- how a floor-plan item is selected.
The Guest provides domain information.
The presentation layer decides how that information appears.
Guest │ │ domain data ▼ Presentation / Adapter │ ▼ User Interface
What the Guest should not own
The Guest should also avoid taking responsibility for objects and systems that belong at higher architectural levels.
It should not:
- own the Event Document;
- own the Group;
- own the Table;
- own the complete affinity graph;
- implement automatic seating optimisation;
- calculate Table geometry;
- define custom-field schemas;
- serialize the complete Event Document;
- format complete reports;
- decide access-control permissions.
The functional objectives of the Guest
Rather than describing individual language-level functions, I prefer to describe the Guest in terms of its required capabilities.
GUEST — FUNCTIONAL OBJECTIVES IDENTITY AND COMMON INFORMATION ─────────────────────────────── Maintain stable object identity Store and retrieve notes Provide a meaningful display name NAMING ────── Maintain anonymous status Maintain display name Maintain title Maintain first name Maintain family name Maintain suffix Produce formatted Guest names Produce initials where required Support consistent title/suffix sorting EVENT INFORMATION ───────────────── Maintain meal preference Maintain gender category Maintain age category Maintain RSVP status Maintain VIP status Maintain seating-lock state Maintain special requirements CONTACT ─────── Maintain individual Contact Information Determine whether email is available Determine whether telephone information is available Use shared Group contact information where appropriate GROUP RELATIONSHIP ────────────────── Retrieve Group association Create or clear Group association Provide Group-related display information SEATING ─────── Retrieve Table association Create or clear Table association Expose seating-lock state CUSTOM INFORMATION ────────────────── Read custom values Update custom values Append custom values Remove custom values Report custom-value count VISUAL REPRESENTATION ───────────────────── Maintain optional Guest image reference PRESENTATION SUPPORT ──────────────────── Provide RSVP presentation information Provide age/gender presentation information Support affinity-related presentation Provide consistent values for sorting/filtering
Intrinsic state and relationships should remain distinct
The class becomes particularly clear when its intrinsic information is separated from its associations.
INTRINSIC GUEST STATE Object Identifier Notes Anonymous status Names Meal Gender category Age category RSVP VIP Seating lock Special requirements Contact Information Custom values Optional image RELATIONSHIPS Group ─────────► separate object Table ─────────► separate object
This distinction becomes important during copying, persistence, import and document loading.
Construction should establish a valid Guest
The Guest should be created with enough information to represent a coherent participant.
The main creation workflow therefore establishes the intrinsic Guest information together.
Conceptually, creation includes:
- Object Identifier;
- anonymous state;
- display name;
- title;
- first name;
- family name;
- suffix;
- meal preference;
- gender classification;
- age classification;
- seating lock;
- notes;
- optional image;
- VIP status;
- RSVP status;
- special requirements;
- Contact Information;
- custom values.
Group and Table relationships can then be established separately.
Why this matters for persistence
This separation is especially useful when loading an Event Document.
1. Load Guests 2. Load Groups 3. Load Tables 4. Build identity indexes 5. Resolve Guest → Group references 6. Resolve Guest → Table references 7. Validate relationships
This avoids requiring all related objects to exist at the exact moment each Guest is created.
The Guest is one of the main integration points in the domain
Although the Guest itself should remain focused, it naturally touches many parts of the Event Document.
Contact Information
│
│
Group ────────────────► Guest ◄──────────── Table
│
│
▼
Event classifications
│
┌──────────┼───────────┐
▼ ▼ ▼
RSVP Meal VIP
│
▼
Special requirements
│
▼
Custom values
This is why the Guest deserves careful modelling.
A weak Guest model would force business rules into UI code and reports.
A coherent Guest model gives those layers a stable domain object to work with.
The same Guest can appear in many different views
The architecture becomes more powerful when one Guest can be represented differently depending on the question the user is asking.
ONE GUEST │ ├── Guest List │ ├── Group View │ ├── Table View │ ├── Floor Plan │ ├── RSVP View │ ├── Meal View │ ├── Affinity View │ └── Reports
The underlying Guest remains the same.
The representation changes.
This is one of the strongest arguments for keeping presentation logic out of the Guest itself.
A Guest is not a Contact
This distinction is fundamental.
A Contact answers questions such as:
How can I communicate with this person?
A Guest answers much more:
PERSON-LIKE INFORMATION
Name
Phone
Email
+
EVENT PARTICIPATION
RSVP
Meal
VIP
Age category
Gender category
Special requirements
Group
Table
Seating lock
Custom event data
=
GUEST
That is why the Guest is a proper domain entity rather than merely a thin wrapper around contact information.
Where the design could evolve
There are several areas where the model could become richer over time without invalidating the current architecture.
Seating could become Chair-based rather than Table-only.
Accessibility requirements that affect automatic seating could become structured constraints.
RSVP could eventually include additional workflow metadata.
Custom fields could gain validation rules and stronger types.
Contact information could gain additional communication channels if genuinely required.
None of those changes require turning the Guest into an entirely different concept.
The design principle I want to preserve
The Guest should remain the authoritative event-domain representation of one participant.
It should know enough about that participant to support the event-planning business rules, but it should not absorb responsibilities that belong to the Event Document, seating engine, presentation layer or reporting system.
The larger architectural lesson
The Guest model illustrates something that is becoming increasingly important throughout this project.
Good domain modelling is not about collecting every possible field into one enormous object.
It is about identifying which information belongs together, which information is shared, which information represents a relationship, and which behaviour should remain at a higher level.
The Guest therefore contains:
the person's event identity;
their structured naming information;
their event classifications;
their individual contact details;
their Group and Table associations;
their custom event-specific data;
and an optional visual reference.
It does not need to know how the entire application works.
That is the role I want this class to continue playing as the application evolves.
Comments
Post a Comment