Congratulations, @jeff! The topic of user data entry, in general, has enough details, if you want them, to become an object of lifetime study and debate.
In this instance, it looks like you still have a number of design decisions ahead. Having concrete criteria can help. (“Best” is not concrete. That is a judgement you can apply once you have criteria.) In fact, at this stage, I’d consider having concrete criteria essential for choosing what’s best.
Some broad suggestions and personal notes… as food for thought.
I find I have maximum flexibility when I decouple the GUI from the DB. That is:
- Load the database record into an in-memory dict.
- Use data bindings, or not, to connect the dict to the GUI.
- On save, write the (edited) dict back to the database.
This gives you the “loose coupling” you were interested in.
For step 2, it is handy to have the dict structured to match the GUI. This is not necessarily a good match to the database structure, however. Steps 1 and 3 can translate between the two kinds of structure.
Validation, where needed, usually occurs somewhere around step 2.5, and is usually tightly integrated with save/cancel/undo/redo logic.
Often, the trick is in deciding how granular you want to make validation/save/cancel/undo/redo. It can be as coarse as an entire data-entry page (all-or-nothing), or as fine-grained as a single entry field, or anything in between.
As a user, I prefer validation to be at the field level. If I’ve made a (catchable) mistake in field 23, I would rather know about it while I’m still at field 23, not 40 fields later.
This is not always practical. When the error is in the relationship between two entered field values (e.g., when two dates must be in a specific order), one cannot say definitively which one is wrong, and one cannot perform a reliable test until both values are known to have been entered. I’d be inclined to perform that test immediately after the 2nd is entered.
Most entry fields have an “on change” event. If you’re validating at the field level, that’s usually a good place to put the validation logic.
As for save/cancel/undo/redo, I tend to think in terms of cohesive objects. If a row or panel’s data entries describe a single, indivisible object (or sub-object), then I would rather have save/cancel/undo/redo on the whole object than on half of it at a time.