Skip to content

Instantly share code, notes, and snippets.

@dealproc
Created May 3, 2015 03:29
Show Gist options
  • Save dealproc/134912a881620fbfc40b to your computer and use it in GitHub Desktop.
Save dealproc/134912a881620fbfc40b to your computer and use it in GitHub Desktop.

I'm trying to figure out how to describe the issue of what everyone calls a CRUD application, and subsequently start to explain why a CRUD app isn't about CRUD, since those screens need validation involved in them more than most developers understand.

Let's look at the acronym, CRUD:

  • C - Create (Insert) values inside the database
  • R - Read (Select) values from the database
  • U - Update (Update :trollface:) values inside the database
  • D - Delete (Delete :trollface:) values from existence

This gives a 1-1 relationship between a users actions and what we do in the database. We're either inserting data (create/insert), displaying those values that we just created (read/select), updating values that exist (update), and when we no longer need the data, we're deleting the data (delete). The hard part with this notion is that in the real world, this is never the case. To support this theory, we should look at one of the most widely accepted CRUD applications that exist out there... a CRM (Customer Relationship Management) system.

In a CRM, we express the thoughts as a series of data elements that make up the existence of the object. The simplest form of this is to express a Contact in the system. Let's go easy on this, shall we:

  • First Name
  • Last Name
  • Email Address
  • Social Security Number
  • Telephone Number
  • Home Address
  • Work Address
  • Alternative Address 1
  • Alternative Address 2
  • Alternative Address 3

Ok, so that's our quint-essential contact card we want to build. In C#, this is easy enough to express:

public class Address {
  public string Street { get; set; }
  public string City { get; set; }
  public string StateIntlOther { get; set; }
  public string PostalCode { get; set; }
  public string Country{ get; set; }
}

public class Contact {
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string EmailAddress { get; set; }
  public string NationalId { get; set; } // let's keep the International Folks happy, shall we?
  public string Telephone { get; set; }
  public ICollection<Address> Addresses { get; set; }

  public Contact() {
    Addresses = new Collection<Address> { get; set; }
  }
}

You notice that, without even batting an eye, we've broken the list of known addresses into its own domain object, and we're providing our contact with this list of addresses. This is on purpose, and keeps it clearer to understand events.

So, what's the quickest way to establish a Contact?

Simplest form would have the fields on our main Customer object, but would not ask you as a user to enter any addresses. That is a different concern. (Follow the idea of an MVP product.) Once this is entered, and the data is stored (this can easily be stored as an event derived from a command), we can then open the Contact's card in an editor window, which would become slightly more complex. This is a good thing, as it prohibits us from overloading the end user with too much information in the beginning.

Upon opening this editor form, we can show the main Contact information to the user which they can edit, but we also can now accept address information for this Contact. An address can be entered in a child form, and subsequently expressed as Commands and eventually Events of the customer!

Case in Point #1

So this is where I need the discussion, and the clarification. When we begin updating a customer, or an address... what should this look like? The working theory I have right now is that this information should realistically be a delta of its predecessor. To explain further, I'm saying that if I open my customer's object from the data store, just to change his or her first name, the entire data store should not be changed, but only the FirstName value. The same for an address... If I'm just changing the Postal Code because the US Gov't decided that it wanted to re-draw the postal code boundaries, why am I mutating the entire database? Why not just mutate the postal code field for the address of concern?

So, with that thought, it almost seems as if a read model should be slightly intelligent as it can look at itself and determine what changes were made during a commit phase. This delta calculation should then be stored in a key/value pair, where the key represents the property, and the value represents the property's value. Gasp! this happens quite a bit in JavaScript and nobody bats an eye?! Don't believe me, consider this code:

var customer = {
  firstName: '',
  lastName: '',
  emailAddress: '',
  nationalId: '',
  telephone: '',
};

...
/// in some processing method deeper in your code...

if (!customer['addresses']) {
  customer['addresses'] = [];
}

for (....) {

  /// work to discover and create the address, whatever.

  customer.addresses.push({
    street: '',
    city: '',
    stateIntlOther: '',
    postalCode: '',
    country: ''  
  });
}

Notice where we're asking if a key of addresses exists, and if not, then create the key and initialize it before starting the work against the property? We're already expressing this as a key/value store anyhow, so why are we not expressing our crud apps more like this?

Case in Point #2

Using the same customer object, our business decides now that we can allow the user to change whatever they wish, but any time they alter the NationalId of the customer, that has to go through an approval process to determine if it is indeed a valid change or not.

In a traditional CRUD application, we may act upon this scenario in many different ways:

  • Provide a staging table for an approval process before the "master" customer is updated
  • Only allow this field to be manipulated by certain roles within the system.
  • Accept everything as-is, and tell the administrator to sod-off about it, that we could care less.

Now, obviously the third item we cannot do. So that seems to leave us with two options:

Option 1: Stage the Data for an Approval Process

If we go this route, now we have a minimum 2 sets of tables, another table to express that there is something to be done, and we have a lot more work of maintaing a larger database.

  • What happens when the administrator that has to approve this data says "Nope"?
  • What happens when the administrator does indeed approve this data?

Option 2: Limit who can manipulate this Data

In this scenario, we opt to state that only the administrator can make the change to this now privileged data point. So, now the personnel hired to administer the data now have to run an external process to get this data point changed, which usually consists of one or many emails, phone calls, discussions, rants, etc. outside of our enterprise system, all so this little piece of data can be saved.

Another Option on the Horizon

What would happen if we, instead of doing either option above, extracted the delta of the customer's information + the user's change, and ran that data through a set of rules. If that set came back clean, then package the delta as a command, and send the command to the server for processing... but if there is a change that should require an approval (the NationalId, in this instance), we could still provide to this same end user a seamless use of the system, but the System of Record change would not have taken place... and we would store the "approved" revision Id as well as the "current" revision id, as seen by the users in the system?

The idea of the "Approved" id would be what we consider as the System of Record, where we would use this revision from the event store as the revision to build legal documents, etc. Our "Current" revision Id is the revision number we would then use to show the not so special personnel that are not allowed to commit this national id change... and we just show them a message on screen which states that the data on screen is the most current data we have for the contact, but some elements may not be approved yet by the system administrator, so the data may not appear on legal documents as the user would expect it to be.

Honestly, I'm unsure about this theory... but it's a work-in-progress, as it's something that would help me in many projects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment