After looking in detail at the Guest model, the next logical object in my C++ event-planning reconstruction is the Group.
At first, a Group appears simpler than a Guest. And in terms of stored state, it genuinely is.
The reconstructed Group contains only three pieces of group-specific state: a collection of Guests, a Group name and a separate object containing shared contact information.
But the simplicity of those fields is deceptive.
The Group sits at an important junction in the application. It connects individual Guests into meaningful units, provides shared contact information, contributes presentation and sorting behaviour, and even participates in seating-related calculations.
The Group is itself an Event Object
Like the Guest, the Group derives from the common Event Object foundation.
It therefore inherits two concepts that are deliberately not duplicated in the Group itself:
- a persistent object identifier;
- free-form notes.
This is architecturally significant.
A Group is not simply an attribute stored inside a Guest. It has its own independent identity and lifetime.
Conceptually:
Event Object
│
├── Persistent identity
├── Notes
│
└── Group
│
├── Group name
├── Group contact information
└── Guest membership
That distinction becomes increasingly valuable as the application grows. If a Group were only a string inside each Guest, changing a group name would potentially require updating every Guest independently. Shared contact information would have to be duplicated. There would be no obvious place for group-level behaviour.
Making the Group an object avoids that entire category of problems.
Only three Group-specific fields
One of the things I like about this reconstructed class is how little persistent state it actually requires.
Ignoring the identity and notes inherited from the common Event Object, the Group stores:
- a collection of references to the Guests belonging to the Group;
- a complete Group Contact value;
- the Group's name.
There is no duplicated Guest information inside the Group.
The Group does not contain copies of Guest names, RSVP statuses, meal choices or table assignments. It holds references to the actual Guest objects.
That makes the basic relationship straightforward:
Group
│
├── Name
├── Contact information
│
└── Members
├── Guest
├── Guest
├── Guest
└── Guest
The Guest model contains the corresponding relationship back to its Group. This means Group membership is visible from both sides of the domain relationship.
Why Groups need to be independent objects
A Group could represent a family, but the architecture does not force that interpretation.
It could equally represent colleagues from the same company, a delegation, a wedding party, a club, a school group or another collection of people that should be treated together for some aspects of event planning.
The important idea is that the relationship has meaning beyond a textual tag.
For example, suppose four Guests are members of the same family. Their individual names, RSVP statuses, meal preferences and seating positions remain personal. But they may share:
- a household address;
- a common telephone number;
- a common email contact;
- a preferred grouping during seating;
- a collective label used in reports or invitations.
That shared information belongs naturally to the Group rather than being copied four times.
Group contact information is much richer than Guest contact information
The contact model associated with a Group is considerably more extensive than the individual Guest contact model I described previously.
The reconstructed Group Contact object contains ten textual fields:
- primary address line;
- secondary address line;
- city;
- state, province or region;
- postal code;
- country;
- work telephone number;
- home telephone number;
- mobile telephone number;
- email address.
This difference between Guest contact information and Group contact information is revealing.
The individual Guest model mainly needs direct communication channels. The Group model can represent something closer to a household or organisational contact record, including a full postal address.
Group Contact
│
├── Postal address
│ ├── Address line 1
│ ├── Address line 2
│ ├── City
│ ├── State / region
│ ├── Postal code
│ └── Country
│
└── Communications
├── Work phone
├── Home phone
├── Mobile phone
└── Email
The Group itself can retrieve and replace this complete Contact object rather than manipulating ten unrelated pieces of state directly.
That is another example of composition being preferable to flattening every field into one enormous domain class.
Contact availability is part of Group behaviour
The Group does not simply store contact information. It can answer questions about it.
There is explicit behaviour for determining whether the Group has an email address and whether any telephone number is available.
That may sound minor, but it keeps callers from repeatedly implementing logic such as:
if work phone exists or home phone exists or mobile phone exists then group is contactable by phone
The object itself understands what “has a phone number” means.
There is also an accessor associated with the first address line, showing that selected contact information can be exposed directly where the application frequently needs it.
The Guest collection is a relationship, not ownership evidence
The Group maintains a collection of pointers to Guests.
That tells me the Group knows which Guests belong to it. It does not, by itself, prove that the Group owns the lifetime of those Guest objects.
This distinction matters when reconstructing C++.
A raw pointer can represent ownership, borrowing, observation or simply an old implementation style. I do not want to infer ownership semantics from syntax alone.
The current call references show that the wider Event Document participates directly in adding Guests and retrieving Guests belonging to Groups. That strongly suggests that membership management belongs to the broader document model rather than being treated as a completely isolated Group operation.
So, for now, the safest architectural description is:
Event Document
│
├── manages Guests
├── manages Groups
│
└── maintains relationships
│
▼
Group
│
└── references its Guests
That is more precise than claiming ownership before the remaining lifetime behaviour has been fully reconstructed.
The Group knows how many Guests it contains
The Group exposes its collection of Guest references and can report the number of Guests currently associated with it.
This is straightforward functionality, but it turns membership into a first-class part of the domain model.
Other parts of the application can therefore reason about a Group without manually inspecting implementation details.
A report can ask for the number of members. A presentation layer can retrieve the members. Seating logic can evaluate the Group as a unit. The Event Document can resolve references after loading persistent data.
The generated call relationships show exactly this sort of reuse: the member collection is referenced by Guest creation, Group queries, document-level Group lookups, reference resolution and seating-related logic.
Formatting a Group means formatting its members too
The Group contains several methods dedicated to producing human-readable descriptions of its Guest membership.
It can generate a complete formatted Guest list, optionally sorted.
It can produce a string containing the first names of the non-anonymous Guests.
It can also construct display-name lists using a supplied separator, again with optional sorting.
This tells me that Groups are intended to appear in more places than a simple Group-management screen.
They are likely to need compact descriptions suitable for tables, reports, lists or printed material.
Conceptually, the same Group might be represented as:
The Smith Family Jane, John, Emily and Peter Jane Smith; John Smith; Emily Smith; Peter Smith
The exact formatting depends on context, but the architectural point is that the Group understands how to turn its membership into meaningful presentation text.
Anonymous Guests affect Group presentation
The first-name-list behaviour explicitly excludes anonymous Guests.
This links directly back to the anonymous-Guest concept discussed in the previous article.
An unnamed placeholder Guest still participates in the Group and still counts as a member, but it should not necessarily appear in a human-readable list of people's names.
That demonstrates why anonymous status was modelled semantically rather than represented merely by an empty name string.
Group names have sorting semantics
The Group also contains behaviour for producing a normalised version of its name specifically for sorting.
The method accepts an option controlling whether name particles should be ignored during that process.
This is another small but revealing detail.
Human names and organisational names do not always sort correctly using a simple byte-for-byte or character-for-character comparison. Prefixes, particles and presentation conventions can change where a name should appear in an alphabetical list.
The reconstructed application therefore distinguishes:
Displayed Group name
│
▼
Sorting normalisation
│
▼
Comparable sorting key
Again, this is the sort of detail that would be very easy to overlook if I recreated the software only by copying what was visible on screen.
The Group has a seating-related responsibility
The most interesting Group behaviour from the wider application perspective is a method that determines the seating compatibility status of the Guests belonging to the Group.
The generated documentation explicitly describes that operation in those terms.
This is an important clue about how Group membership influences seating.
The Group is therefore not simply administrative metadata. Its membership is meaningful to the seating domain.
The implementation uses the Group's Guest collection when calculating this status.
I am deliberately not assigning a stronger interpretation to the integer result yet. The documentation confirms that it represents a seating compatibility status, but I still need the surrounding solver and affinity logic before I can safely describe every possible value and how it affects optimisation.
Groups also participate in affinity processing
The generated call graph provides another useful connection.
The Event Document accesses the Group's Guest list while determining whether any Guest in the Group has a non-neutral affinity relationship.
This means Group membership becomes a useful boundary for querying interpersonal seating constraints.
Conceptually:
Group
│
├── Guest A ── affinity relationships
├── Guest B ── affinity relationships
├── Guest C ── affinity relationships
└── Guest D ── affinity relationships
│
▼
Group-level query by
the Event Document
This reinforces a pattern I am starting to see in the architecture: the Group stores membership, while higher-level document logic can use that membership to perform broader relationship analysis.
The Group also has a visual identity
The reconstructed class includes static behaviour for producing a Group icon.
This does not mean that the Group itself is a graphical floor-plan object. It is not.
Instead, it means that the application has a standard visual representation for Groups in lists, dialogs or other interface components.
This distinction is worth preserving:
Group
│
├── Domain object
├── Not a floor-plan object
│
└── Can still have
a UI icon
Having an icon does not automatically make something part of the graphical scene hierarchy.
The Group and Guest form a bidirectional relationship
Looking at the Guest and Group together makes the domain structure much clearer.
The Group maintains its collection of Guests.
Each Guest can also refer to the Group to which it belongs.
So conceptually the architecture contains a bidirectional association:
belongs to Guest ─────────────────────► Group ▲ │ │ │ └──────── contains ─────────┘
Bidirectional relationships need careful handling because both sides must remain consistent.
If a Guest moves from one Group to another, the Guest's relationship and both Group membership collections potentially need updating as one logical operation.
The current documentation suggests that the Event Document plays an important coordinating role here. That is an architecture point I expect to examine more closely when I document the Event Document itself.
What the Group does not contain is equally important
The Group class is useful partly because it does not try to duplicate the Guest model.
It contains no meal preference of its own, no RSVP status, no age category, no gender category, no table pointer and no custom Guest fields.
Those remain Guest responsibilities.
Likewise, the Group itself does not store affinity relationships. Higher-level logic can inspect its members when affinity information is required.
This keeps the boundary relatively clean:
Guest owns individual state
│
▼
Group owns shared context
│
▼
Document coordinates
cross-object relationships
Putting the complete Group together
The reconstructed Group can now be summarised conceptually as follows:
Group
│
├── inherited
│ ├── persistent identity
│ └── notes
│
├── identity / presentation
│ ├── Group name
│ ├── formatted display name
│ ├── normalised sorting name
│ └── Group icon
│
├── membership
│ ├── Guest references
│ ├── Guest count
│ ├── formatted member list
│ ├── first-name list
│ └── display-name list
│
├── shared contact information
│ ├── address line 1
│ ├── address line 2
│ ├── city
│ ├── state / region
│ ├── postal code
│ ├── country
│ ├── work phone
│ ├── home phone
│ ├── mobile phone
│ └── email
│
├── contact queries
│ ├── has email?
│ └── has any phone number?
│
└── seating context
└── Group seating compatibility
A compact class with a large architectural role
The most interesting thing about this class is the contrast between its data size and its architectural importance.
The Group itself stores only three Group-specific members.
Yet through those three members it connects identity, shared contact information, Guest membership, formatting, sorting, affinity queries and seating compatibility.
That is a useful object-oriented design lesson.
A class does not need dozens of fields to represent an important domain concept. Often the value lies in the relationships and invariants surrounding a relatively small amount of state.
The reconstruction is beginning to expose a fairly coherent object model: identity at the bottom, individual Guests and Groups above it, and a document-level model responsible for assembling those objects into a complete event.
The next interesting step will be to move further into the physical side of the problem: tables, seats and the relationship between people and the seating plan.
Comments
Post a Comment