Possible solutions to this problem will largely depend on what changes you allow in the database while the user is editing the data.
In other words, as soon as it “leaves” the database, it is blocked exclusively for the user, or can other users or processes update it during this time?
For example, if a user can receive data and sit on it for several hours or days, but the database continues to allow data updates, then you really want to track the changes made by the user to the database currently, and not the changes made by the user to The data they are viewing.
The way we process this scenario is to start a transaction, read the entire existing object, and then use reflection to compare old and new values, recording the changes in the audit log. This gets a little complicated when working with nested records, but it’s worth the time.
If, on the other hand, other users or processes are not allowed to modify the data, then you have several different parameters that vary in complexity, data storage and impact on existing data structures.
For example, you can change each property in each of your classes to record when it has changed, and save the current evaluation of these changes in the class (obviously, implementing a base class helps a lot here).
However, depending on the point at which you commit user changes (each time they update a field in a form, for example), this can generate a significant amount of irreplaceable log information, because you probably only want to know what has changed in terms of databases, not in terms of user interface.
You can also deeply clone an object and transfer it around layers. Then, when it is time to determine what has changed, you can use reflection again. However, depending on the size of your business objects, this approach may impose a hefty execution penalty, since the full copy must be moved along the wire and saved with the original record.
You can also implement the same approach as the "Updates allowed during editing" approach. This, in my opinion, is the purest solution, because the source data should not move with the edited data, there is no way to fake the source data and supports many clients without supporting tracking changes in the user interface level.