In my previous post about this personal C++ project, I described why I had chosen table and seating organisation as the problem I wanted to work on.
The next question was more fundamental: what are the objects?
It is tempting when developing a desktop application to start with the visible parts. Create the main window. Add a guest list. Add a table editor. Put some buttons around them. Connect a few signals and slots, and very quickly there is something on the screen that looks like progress.
I am deliberately trying not to begin there.
Before concentrating on the screens, I want a reasonably coherent model of the problem itself. The user interface should eventually manipulate that model; it should not accidentally become the model.
Starting with the domain rather than the windows
The first class structure is now sufficiently developed that I can begin to see the shape of the application.
At the centre is a general event document. This is the working representation of an event being planned. Around it sit the principal business concepts: guests, groups, tables, seats, seating information and the physical objects that make up a floor plan.
The current conceptual structure looks approximately like this:
Event document
│
┌─────────────┼─────────────┐
│ │ │
Guests Groups Seating
│ │ │
└──── relationships ────────┤
│
Tables
│
Seats
Floor-plan objects
│
┌──────────┬────────┼────────┬─────────┐
│ │ │ │ │
Tables Walls Shapes Text Images
This diagram is deliberately simpler than the actual C++ hierarchy. At this stage I am more interested in communicating the responsibilities than publishing implementation names.
It also reveals something important about the problem: a table is not merely a row in a list.
A table participates in at least two aspects of the application. It is part of the seating model, because it contains seats to which guests can be assigned. But it is also part of the physical layout, because it has a graphical presence in the room.
That dual role is one of the architectural questions I expect to keep refining.
A common identity for the important objects
One decision in the current design is to have a common abstraction for important event objects.
Guests and groups derive from this common object concept. The graphical entities used to construct the floor plan do as well.
The purpose is not inheritance for its own sake. I want objects that participate in the event to have a consistent identity and a common behavioural foundation where that genuinely makes sense.
The current hierarchy can be simplified as:
Event object
│
├── Guest
│
├── Group
│
└── Graphical object
│
├── Table
├── Wall
├── Shape
├── Text
├── Floor-plan image
└── Colour key
I like this separation because the distinction is semantic.
A guest is an event object, but it is not a graphical object. A wall is an event object and it is graphical. A table is both a domain concept and something that must be positioned and rendered on the plan.
The base graphical abstraction therefore gives me somewhere to place behaviour shared by objects that occupy the visual workspace without forcing guest-related behaviour into the same branch.
Guests and groups are different concepts
The distinction between a guest and a group is also intentional.
A guest represents an individual participant. A group represents a collection or association of guests that needs to exist independently in the event model.
A family attending a wedding is an obvious example, but I do not want the architecture to assume that every grouping is necessarily a family. A group might represent colleagues, an organisation, a delegation or another meaningful collection.
The current model also contains separate concepts for contact information associated with individuals and groups, guest properties, custom guest information and affinity information.
That last concept is particularly important because seating is not simply a capacity problem.
If the only requirement were to fit 120 people into 15 tables of eight seats, the problem would be trivial. Real seating arrangements include preferences and constraints between people.
Some guests should preferably sit together. Some may need to be close to each other. Others may need to be kept apart. Groups should perhaps remain together where possible, but sometimes splitting a group may be preferable to violating a more important constraint.
So the eventual seating engine needs more than:
Guest → Seat
Conceptually it is closer to:
Guests
│
├── group membership
├── properties
└── affinities / constraints
│
▼
Seating process
│
▼
Tables and seats
│
▼
Seating solution
This is where the application begins to become algorithmically interesting.
Separating seating from the event document
Another part of the current hierarchy that I find useful is the existence of a separate seating model.
The event document builds on that seating capability, but a seating solution can also build on the same abstraction.
That suggests a useful conceptual distinction between the event as it currently exists and a candidate arrangement produced while trying to improve the seating.
I do not want an optimisation algorithm to need to manipulate the complete user-facing document every time it evaluates another possible arrangement.
A solver may eventually evaluate a very large number of candidate arrangements. Keeping the essential seating representation conceptually separable from the complete document gives me room to develop that part of the application independently.
The current documentation already contains a genetic solver concept. I am not treating that as proof that a genetic algorithm will necessarily remain the final or only optimisation technique. At this point it represents one direction for solving what is fundamentally a constrained optimisation problem.
The floor plan is its own small object system
The graphical side is already developing into a subsystem of its own.
A floor plan needs much more than tables. It can contain walls, arbitrary shapes, text, images and visual keys. Those elements share enough behaviour to justify a graphical-object abstraction while still representing very different things.
This also fits naturally with Qt's Graphics View framework.
The current design has separate graphical representations for several of these domain objects, including tables, seats, walls, shapes, text and images. There are also separate graphical scenes for the floor-plan view and the table-plan view.
That distinction is useful because I do not necessarily want the persistent domain object itself to become a Qt graphics item.
I would rather be able to think in terms of:
Domain object
│
│ represented by
▼
Graphics item
│
│ displayed inside
▼
Graphics scene
│
│ presented through
▼
Graphics view
That gives the GUI responsibility for presentation and interaction while allowing the underlying event model to retain its own meaning.
It should also help later with printing, exporting and alternative presentations of the same information.
Views are beginning to form another layer
The documentation also shows a common view abstraction with several specialised presentations built around it.
There are list-oriented views, graphical views, text-oriented views, dashboard-style views and affinity views. Several of these are paired with separate filtering components.
This is a direction I want to preserve: the same underlying event should be capable of being presented in several different ways without each presentation owning its own copy of the business data.
A guest can appear in a list, in a chart, in a seating plan and in statistical information. Those are presentations of the same guest, not four different guest models.
Likewise, changing the active filter should affect what the user sees rather than redefine what exists in the event.
This distinction sounds obvious when written down. In GUI applications, however, it is remarkably easy for application state to migrate into widgets until the only way to understand the business state is to inspect what happens to be displayed on screen.
Import, export and documents are already visible in the design
Although data integration is not my immediate development priority, I have deliberately left space for it in the architecture.
The current class set already contains concepts for import mapping and several import and export workflows. It also contains separate document abstractions for XML and HTML-oriented output.
This reinforces something I mentioned in the previous article: persistence, interchange and presentation are related, but they are not necessarily the same problem.
The application may ultimately use one representation for its native event files while producing HTML, PDF, contact information or other formats for external consumption.
I want those decisions to emerge from the responsibilities of the model rather than from whichever Qt API happens to be easiest to call first.
Why I am documenting this so early
I am also generating Doxygen documentation while the application is still being designed.
That may seem excessive for a personal project, but it serves a practical purpose.
Once the classes are shown as inheritance trees, dependency graphs and collaboration diagrams, architectural mistakes become easier to see. A class that appears perfectly reasonable in isolation can suddenly look suspicious when a diagram shows it connected to half of the application.
The documentation therefore becomes another design tool.
It also forces me to answer a deceptively difficult question for every important class:
What is this object actually responsible for?
If I cannot describe that clearly, there is a reasonable chance that the class itself is not yet clear enough.
This is the starting point, not the finished architecture
I am under no illusion that the current structure will survive unchanged.
Some classes will probably disappear. Some responsibilities will move. Some inheritance may turn out to be better represented through composition. Objects that currently look independent may prove to belong together, while others may need stronger separation.
That is normal.
What matters to me at this stage is that I now have a coherent enough model to begin implementing behaviour without making every feature an isolated experiment.
The project already has identifiable concepts for the event document, guests, groups, contacts, affinities, tables, seats, seating solutions, graphical objects, floor plans, views, filtering, printing and data interchange.
The next challenge is to make those abstractions earn their place in actual code.
For now, however, I have something I did not have when I started thinking about this project several years ago.
I no longer have only an idea for a program.
I have the beginnings of an architecture.
Where relevant, factual and regulatory references were checked against the sources cited in the article. AI assistance does not replace professional legal, regulatory, financial or technical advice, and the final selection, interpretation, opinions and conclusions presented here remain my own.
Comments
Post a Comment