I’ve read several questions and I couldn’t find one specifically similar to mine. I also don’t think there’s a design pattern for this situation as I think my system is a legacy system and don’t fit the “best practices” in many parts of it.
I’m rewriting a legacy feature from a 1990’s system to a “new” system in .net, but I have to keep using the same database and data structures. So, I cannot reinvent the wheel regarding separating data access, OOP, etc. It’s a WinForms app that will be able to query/change the database. And we have very few programmers, so I want to keep the code simple, to make maintenance easy in a distant future.
Now the question:
The feature I’m working on, after the user hits “Save” button, it has several steps and conditions, including some parts where we need to get the user’s Yes/No input and continue based on that. For instance (fictional pseudo-code):
public void save_click() {
var transaction = ...; // code to start a transaction
if (cmbStatus = "Close")
{
// validations.
...
var completed = DataAccessLayer.GetStatusForOrder(txtOrder);
if (completed) {
var userInput = MessageBox.Show("Order is completed, continue anyway?", YesNoButtons);
if (userInput = No) {
transaction.Rollback;
return;
}
// means user hit Yes
DataAccessLayer.InsertToAlarmsTable(...);
// validate delivery:
var deliveryDate = DataAccessLayer.GetDeliveryDate(txtOrder);
if (deliveryDate < DateTime.Now) {
MessageBox.Show("wrong date. Cannot close");
transaction.Rollback;
return;
}
} else {
// not completed
DataAccessLayer.ADifferentUpdateToAnotherTable(...);
}
} else if (other status) {
// in here other conditions and other table updates
if (txtTax = 100) {
// a msgBox that warns the user but continues anyway:
MessageBox.Show("Warning: tax is 100, check with Sales after the update.");
DataAccessLayer.InsertAlarmsTable(...);
}
}
if (txtPrice > 1000) {
MessageBox.Show("Too expensive.");
transaction.Rollback;
return;
}
// at the end, do the main Update
DataAccessLayer.UpdateOrderTable(...);
DataAccessLayer.UpdateTaxTable(...);
if (some other stuff) {
// some other updates
}
// if all above was successful, commit:
transaction.Commit();
}
My problem with it is that it’s becoming too monolithic and these MessageBox and transaction.Rollback and early returns are making me nervous. It does not feel right. You know?
I’m in search of a way to chain these validations and conditions in a way that I can choose what to do next based on previous validation result. And still be able to warn the user when needed and get user input when needed.
My first idea: separate methods returning (bool, string)
My first solution to this was to have separate methods returning a tuple of (bool success, string messages), but I didn’t like all the ifs to continue:
if (status = "Close") {
var v = ValidateStatusCompleted(...);
if (!v.success) {
transaction.Rollback();
MessageBox.Show(v.messages);
return;
}
v = ValidateDeliveryDate();
if (!v.success) {
transaction.Rollback();
MessageBox.Show(v.messages);
return;
}
... and so on
}
That improved a bit, but was still too verbose on non-business logic stuff. So it led me to:
Second idea: Exceptions
I think using exceptions make it look much better:
var transaction = ...;
try {
if (status = "Close") {
ValidateStatusCompleted(); // This has a Yes/No msgBox inside it
ValidateDeliveryDate();
} else if (otherStatus) {
ValidateSomethingElse();
} else if (other) {}
DoSaveToDB();
transaction.Commit();
} catch(ApplicationException exc) {
MessageBox.Show(exc.Message);
transaction.Rollback();
}
But then if you invest too much time reading stuff online, you’ll end up finding people saying that Exceptions should not be used for business logic control.
So I wanted to know if there’s some other design pattern that I don’t know and I could be using here to solve my problem while keeping the code simple.
I don’t want this question to be too subjective, I’ve tried my best to make it look like a real-world scenario that other people might be having as well. I just want to know if there’s some easier way to solve it that I’ve never heard of before.
3