So I have an object graph, let's just say it's an order. You have the order class, line item class, tracking number class, payment class. You get the idea.
Now the business requirement is any user can change the order, but order changes must be approved by the manager. Until the manger approves nothing changes. Managers can change anything, at any time, without approval.
What are the best practices for dealing with situations like this? Saving the many (possible) different states of the order object and eventually approving or rejecting the changes.
i'm using C# and Nhibernate.
Thanks, Kyle.
I would create a transaction table. It would have a record for each pending change. It would reference the order table.
So an order would get created but have a pending change; a record would be inserted into the orders table, with a status column of pending, and a record would be insterted into the OrderTransaction table.
For every change another record would get inserted into the OrderTransaction table.
I would also set up a RequestedChanges table with all the possible requested changes.
Similar to Sam WIlliamson's idea about a transaction table, I would use a temporary table.
Changes made by someone who is not a manager, go to new Order objects in the temp table. The manager will have an interface to review these orders pending approval, and the system will have all the changes saved already, but outside of the standard position.
This might be an issue for the user's interface as well, they will have to see both the official version of an order and the pending-revision version side by side to make sense of the state of the object.
Anyway I think your best bet is to store the objects as normal, but in a separate table from the official records, pending review by the manager. This temp table should never grow very large, as it represents the backlog of approvals the manager has to get to.
I do not have any experience with nHibernate.
For a scenario like this, it is better left to database to store Order (state = ForManagerToApproveOrReject) and it can then be queried to see which Orders are waiting for approval/rejection (from manager's view)
A manager can then either approve/reject it.
The inheritance mode of saving Order (ApprovedOrder, RejectedOrder) seems little odd.
a simple solution is to just make another order that is a copy of the original with the changes applied, the status set to PendingApproval, and an auto-increment VersionNumber. change the table's primary key to include ApprovalDate. Then the current, approved order is always the one with the most recent approval date.
If orders are not complete until approved you may want to have a pending orders, and completed orders table structure. Pending orders might just be serialized objects and only write it out to order, order lines etc once approved.
If you allow changing orders after approval it become more complicated, you may also need to take into account post approval steps, payment received, picking, packing, shipping etc.
There are lots of ways to do this type of thing, how you do it will really depend on what the real business requirements are. i.e. you say managers can change orders at any time, but should they really be allowed to change a shipped order?
I understand that part, the issue I'm having is figuring out how/where to save all the changes while the order is not approved. For instance, User A adds a payment, user B changes the address and User C adds a new line item.
until the manager approves the order stays as it was originally created (or retrieved from DB). Once manager is in the approval screen he/she can approve/reject each change. Changes are written to the original order and an audit is kept. User A changed xxx at yyy approved by zzz at aaa.
What you are describing is a workflow, it actually sounds to me like a good candidate for Windows Workflow Foundation. If your workflow is business critical I would be inclined to separate it out from your database logic, WWF will allow you to do this.
thanks for all the answers. The actual business use case is not creating orders, but if I tried to explain the actual business I'd have to write a couple paragraphs. The bottom line is managers need to moderate changes on a deeply nested object graph, before they "go live."
I like the transaction table idea.
I also like saving two versions of the "order," one with changes, one without.
I guess I'll have to dive into both and see what happens.
Related
Consider I'm having a domain entity of User. I want to identify this user by (fingerprint) a set of properties such as IP, email, phone, user-agent. Now since by DDD principles a Fingerprint can't be an entity, so I defined this as a ValueObject. Each time when a user tries to make a transaction I make a lookup for a matching fingerprint to associate a user with the request.
Ef core suggests us using OwnsMany() for ValueObjects. My problem is that this collection of owned entities are loaded immediately without any pagination. I may load 100 users per page and each of which may have hundreds of fingerprints because each time IP or user-agent changes I have to create a new one.
My questions are
Is there a way to paginate those fingerprints? I can't do it, because the repository has a constraint for aggregate roots only.
Can I actually use OwnsMany ValueObjects for situations when there are more than 1k objects?
If not, how do I solve this problem?
I do not know your whole domain model, so I assume there is a reason why User is an entity and contains a collection of Fingerprints.
Each time when a user tries to make a transaction I make a lookup for a matching fingerprint to associate a user with the request.
You should rather not load all records from a database into your application memory. As I understand, what you really need when User is making a transaction, are not all the fingerprints, but only the matching one. A solution would be to properly prepare your object when you are loading it from your database. Do not load all the Fingerprints, but only the one you are interested in. You can make another property for that in your User class. Write a Linq query or SQL select in your infrastructure layer.
This solution might cause another problem. Every time you add a new Fingerprint, you will have that redundant property in your User class that you don't need. When you add new Fingerprint, this property will not update automatically. If you do not feel comfortable with it, I would suggest you to seperate your read and write models. You can use Tactical DDD patterns for saving, but probably you don't need them for simple reads.
I have the following DDD scenario, grouped into the following aggregates:
User,
Friends (User Associations),
File (for user uploading),
Galleries (grouping of files),
Messages (user communication),
Groups (users can create and other members can join),
GroupMessages (messages sent to all members of a group),
GroupForums (group members can discuss various topics)
This is where it gets confusing. A user is associated with everything down to GroupForums. It seems illogical to have to go through the User repository to access the other aggregates although, from a cascading standpoint, if I removed the user, technically, the records associated with the user should go away as well.
It seems as if I should not add all of the one-to-many associations that exist here to the user entity either, as hydrating from the database seems to be ridiculous, especially if I try pulling every record associated with the user. What is the recommended strategy for organizing your aggregates, and repositories as well as proper way of dealing with a lot of one-to-many relationships for a given entity?
The fact that you used the word 'associated' in your sentence "A user is associated with everything..." is quite a clue. It is absolutely fine for aggregate roots to be associated or even for one to 'belong' to another. However, you need to look at whether an entity can exist without the AR. If it can it probably has its own life-cycle and should be an AR. If it can not it is part of the aggregate. This can be tricky to distill.
You need to have a very clear boundary around your ARs. For example, even though a Forum may require a User to create it this does not mean that the Forum needs to (or even can) be deleted when the user is deleted. So the User in the Forum may become, say, the ForumCreator (a value object) that contains the user name and id only. When the User is deleted then the forum can continue its existence.
In the Order/OrderLine/Product scenario it would not make much sense to delete all order lines that contain a specific product if you choose to delete it. I know that a product probably should never be deleted but we'll use it as an example. You would simply have the relevant product data 'denormalized' into the order line, e.g.: product id, product name. So even if the product name happens to change it does not mean that all order lines need updating, or even should be updated. In fact, the order line represents a point in time and the 'original' product name should be retained. The purchaser may have ordered 'Some lirril product' and then the name changed to 'Little product'. Not the same thing although it is the exact same product. The purchaser only remembers the original.
I hope that makes sense and helps in some way. You definitely need to find those hard edges to your object graph to get to the real aggregates.
In IDDD book, Aggregate is the consistency boundary, if these entites have no transactional consistency needs, they may be different Aggregate. We should not design aggregate by the dependencies. If so, you will have large-cluster aggregate.
I'm wanting to stop two users accidently overwriting each other when updating a record. That is to say two users load a page with record A on it. User one updates record to AB and user two updates it to AC.
I don't just want the last to hit the database to override. I need a mechanism to say the record has been updated so yours can't be saved.
Now the two ideas I have is to time stamp the records and check that. If it doesn't match up don't allow the update. The second method is to GUID the record each time an update is performed, check the GUID and if it doesn't match don't update.
Are either of these methods valid, if so, which is best. If not, what do you suggest. This is in C# if it makes a difference
Thanks
The two methods you've mentioned are effectively equivalent - either way you've got a unique identifier for "the record at the checkout time" effectively. I don't have any particular view on which is better, although a timestamp obviously gives you the added benefit of data about when the record was valid.
An alternative is to remember what the previous values were, and only allow an update if they match - that way if user A started editing a record, then user B goes in and changes something, then changes it back, user A's edits are still valid.
The term for these techniques is optimistic locking (or optimistic concurrency control).
There is actually a third method. To do the update, issue an update statement of this form:
UPDATE table SET update_field = new_value
WHERE db_pk = my_pk // assume primary key immutable
AND update_field = original_field_value
where original_field_value is the value of the field before the update was attempted. This update will fail if someone else has modified update_field, unless they have changed it to the same value that you have.
You're describing Optimistic Locking, a valid and useful technique.
See references here.
Either method is valid for checking.
As to which is the best you have to look at the size of your app and how long it will take to implement each one. So if this is only ever going to happen occasionally then I'd prob go for the quicker solution and implement the timestamp option.
If you want something more detailed google concurrency - heres an article to start with - concirrency
I am using the first option. Update the timestamp on each update. So at the time of update we check the equality of the timestamp.
Do the terms optimistic and pessimistic locking ring a bell. These are the two recognised approaches to the problem you are describing. It sounds like you are working in a web environment. In this case the former option (optimistic locking) is more appropriate. You have gone on to describe how this would generally be implemented. It is common to use a timestamp or a version number to check if the record has been updated since the record was retrieved. One other thing to consider is to let your users know that there has been a change to the underlying data and potentially give them the option to choose between what they have attempted to save and what was save by another user. This choice really depends on what the business rules are.
I am working on a project where I am importing in orders from an external vendor. I will need to validate the information prior to loading it into our ERP system and then send a response with shipping information once we have processed and shipped the order.
I wanted to see how others would track the steps necessary to make this happen. Not looking for code just wanted to get an idea of how others would track where they are at in the process? Do you write records for all the necessary steps..do you use flags, etc.
We currently use C#, Oracle DB, and BPEL
Process Steps:
Import order information into staging table.
Validate order information (as much as possible) prior to loading into ERP system.
If validation fails send notification, if passes send to ERP.
Perform further validation on order (sufficient QTY, etc). If fails send notification to vendor...if passes let proceed through to shipping.
Ship Order.
Just wanted to see how others would approach tracking these steps?
Any info/suggestions would be greatly appreciated.
--s
Well, you want to hold as much information as possible, probably. I'd use a state-based system to determine where the object is.
The next question is do you wish to optimise it by having the objects of various states in different tables. It's good, because it means queries are faster (no where clauses), it's bad, because you need to duplicate tables (i.e. columns).
Probably, I'd have one table, something like 'tblInProgressOrders' and 'tblAcceptedOrders' (whatever names you wish). In the 'InProgress', it would have a 'CurrentState' what determins what is what. In accepted orders you may be a bit of metadata, but it's implied that if it's in there, it's accepted.
HTH.
May be my title is not clear. I am looking for some kind of version control on database tables, like subversion does on files, like wiki does.
I want to trace the changes log.
I want to extract and run the diff in reverse. (undo like a "svn merge -r 101:100").
I may need a indexed search on the history.
I've read the "Design Pattern for Undo Engine", but it is related to "Patterns". Are there anything I could reuse without reinvent the wheel?
EDIT:
For example, bank account transactions. I have column "balance"(and others) updated in table. a user will find a mistake by him 10 days later, and he will want to cancel/rollback the specific transaction, without changing others.
How can I do it gracefully in the application level?
Martin Fowler covers the topic in Patterns for things that change with time. Still patterns and not an actual framework but he shows example data and how to use it.
You could use a revision approach for each record that you want to trace. This would involve retaining a row in your table for every revision of a record. The records would be tied together by a shared 'ID' and could be queried on the 'Revision Status' (e.g. Get the latest "Approved" record).
In your application tier, you can handle these records individually and roll back to an earlier state if needed, as long as you record all the necessary information.
[ID] [Revision Date] [Revision Status] [Modified By] [Balance]
1 1-1-2008 Expired User1 $100
1 1-2-2008 Expired User2 $200
2 1-2-2008 Approved User3 $300
1 1-3-2008 Approved User1 $250
Pedantic point. Your bank account example would not get past an auditor/regulator.
Any erroneous entries in an account should be left there for the record. An equal and opposite correction transaction would be applied to the account. In effect rolling back the original transaction but leaving a very obvious trace of the original error and its correction.
I'd go with a bi-temporal database design, which would give you all the data required to perform and rollback, whether that means inserting more rows or simply deleting the later modifications.
There's a fair amount of subtlety to such a database design but there's are very good book on the subject:
Developing Time-oriented Database Applications in SQL by Richard T. Snodgrass
available for download here:
http://www.cs.arizona.edu/people/rts/tdbbook.pdf
Using a database transaction would be a bad idea because the locks it would create in the database - basically database transactions should be as short as possible.
Anything in the application layer, unless it has some persistence mechanism itself, won't survive application restarts (although that might not be a requirement).
Based on your comment to James Anderson, I would have the user interface write a new insert when cancelling a transaction. It would insert a new record into the table that had the same values as the cancelled transaction except the value would be a negative number instead of a positive number. If you have a structure that includes something to define the purpose of the transaction, I would make it say cancelled and the record number of the transaction it was cancelling.
Based on the various comments a possible solution for your problem would be to make a "date effective" table.
Basicly you add valid-from-date and valid-to-date columns to every table.
The "current" record should always have a valid_to_date of "2999-12-31" or some arbiteraly high value.
When a value changes you change the "valid-to-date" to the current date and insert a
new row with a valid-from-date of today and a valid-to-date of "2999-12-31" copy all the columns from the old row if they have not been changed.
You can create views with
"select all-columns-except-valid-xx-date from table where valid-to-date = '2999-12-31'"
Which will allow all your current queries to work unchanged.
This is a very common tecnique in data warehouse environments and for thing like exchange rates where the effective date is important.
The undo logic should be obvious.
I'm not aware of a specific pattern, although I have set up full undo/audit histories before using triggers and rowversions.
There are a couple of apps for MS Sql that let you trawl through the logs and see the actual changes.
I've used one called Log Navigator back with MS SQL 2000 that used to let me undo a specific historical transaction - I can't find it now though.
http://www.lumigent.com and http://www.apexsql.com do tools for viewing the logs, but I don't think either lets you roll them back.
I think the best way to do this is to write your application with this in mind - which you have a couple of good suggestions here already on how to do.