CRUD Interface for a Service - Is a bad practice ?

I have been reading in different places that using a CRUD (Create, Read, Update, Delete) interface for a service is considered a bad practice or anti-pattern. Therefore, this kind of interface should be avoided at all cost.

I am not agree with this at all, when I hear the word CRUD, I assume something like this:

  • CreateCustomer (Create new customer)
  • ModifyCustomer (Modify an existing customer)
  • DeleteCustomer (Delete an existing customer)
  • FindCustomer (Find an existing customer)

This is typical case of a CRUD interface to manage data related to a customer entity.

What happens if those services represent real business events in the system that I am trying to model, and the design of them also adheres to the four tenets of service orientation, is that considered a bad practice as well ?.

I do not think so.

The sample given in this article "Principles of Service Design: Service Patterns and Anti-Patterns"  shows an ugly sample of CRUD interface, and it is ok to consider it an anti-pattern. However, not all the CRUD interfaces are modeled in that way, in fact, I have never seen such a bad design as the one shown in that sample.

What do think ?. I would like to hear any opinion about this topic.

Published 25 January 2007 10:46 AM by cibrax
Filed under: , ,

Comments

# AndrewSeven said on 25 January, 2007 12:45 PM

I would have to agree, he is giving crud a bad name ;)

In the CRUDy interface he describes, 4 out of the 5 methods are not really part of Create/Read/Update/Delete; the problem is not the CRUD, its the overall design.

# Steve said on 25 January, 2007 12:59 PM

I agree, CRUD gets a bad wrap.  Services not using CRUD can be equally bad given a horrendous design.  If you have a design that is solid and happens to look CRUDy, who cares?  Perhaps the business needs require CRUD?  After all, what systems in existence today don't need something that provides CRUD operations?

# Robert Wilczynski said on 25 January, 2007 01:06 PM

Pablo, I have to say I agree with you that "some kinds of CRUD" are perfectly fine (as far as CreateCustomer is the only thing needed to create the customer). What I would consider an anti-pattern would be the following code:

service.CreateCustomer(c);

foreach(Group group in c.Groups)

  service.AddCustomerToGroup(c.CustomerId, group.GroupId);

foreach(Person person in c.Contacts)

  service.AddCustomerContact(c.CustomerId, person);

I would say that naming operations with a Create, Read, Update or Delete prefix doesn't automatically make the contract CRUD'y as long as those operations either encapsulate some kind of unit of work (creating a customer, adding customer to groups, adding customer contacts etc.) or are simple and don't require other (or a lot of other) calls to complete the unit of work.

If CreateCustomer would add the customer to specified groups and create customer contacts in one call, even though the name would suggest CRUD - this operation would be more than a simple create.

I think that the term "CRUD interface" is somehow misleading - chatty contract/interface is something that should be avoided.

# Oran said on 25 January, 2007 02:22 PM

I agree with Robert about operations mapping to units of work.  Sometimes these units are simple and therefore map cleanly to CRUD operations.  But I prefer to use businessy names for the CRUD operations where possible, so instead of CreateOrder, it might be PlaceOrder.  Instead of DeleteOrder, it would be CancelOrder.

These seemingly minor wordsmithing differences (similar to Behavior Driven Development replacing "test" with "behavior") are part of a larger shift in perspective away from the shared database integration style and more towards the message-based business contract style.  It's too easy to literally interpret CRUD intefaces as the new "servicey" protocol for tunneling data row synchronization between systems.

I'm curious how RESTafarians weigh in on this subject.

# KjellSJ said on 30 January, 2007 11:54 AM

Robert and Oran makes very valid points; the services you publish should be business operations that get invoked as a result of a real-life business event: e.g. "CustomerHasMoved" is a real-life event, "UpdateCustomer" is a CRUDy procedure for modifying the data store. What if the customer's insurance policy needs to be updated in response to the business event ? How do you convey the business sematics with data centric services. This is especially true for all operations that you plan to provide as building blocks for orchestration and coreography in EDA solutions. But of course, you need core entity services and master data management to make SOA work; and these core services will be sort of CRUDy. My further thoughts about published services: http://kjellsj.blogspot.com/2006/12/service-architecture-service-oriented.html

# Benjamin Carlyle said on 03 February, 2007 01:51 AM

Let's write your example another way:

   * POST http://example.com/customerFactory

   * PUT http://example.com/theCustomer

   * DELETE http://example.com/theCustomer

   * GET http://example.com/customerFinderForm

You get a few advantages:

* Now it works with a web client

* It's clear that you are missing a few methods, like "GET theCustomer"

* You can separate the definition of what a customer document looks like from the main baseclass (ie the resource baseclass), and allow it to evolve over time

* These resources integrate easily with other services that use the same customer document type

* Proxies can be introduced easily to control access, do caching, etc, as they can easily determine by method and a url regex what category a particular request falls into.

I think the problem with the original example is not the resource-level CRUD approach but applying CRUD at too low a level. Normally a request to a resource should be a transaction in and of itself, but splitting everything into database-cell-like updates is definitely a no-no.

Benjamin.

# Softwaremaker said on 14 March, 2007 10:55 PM

While I agree that John's sample is a bad choice, I still dont think a CRUD makes a good interface for a good (business) service. This may make good case of composite services but a "good", well-defined, discoverable service that is useful for business, SO-like consumption should look like this

1) DischargePatient, which is then made up of

--- DeletePatientFromHospitalStay

--- CreateBedManagementRecord

--- ModifyPatientBill

--- or something to that effect...

It is important to note that the sub-services may not be talking WS-* and it shouldnt be if its too chatty. Of course, I am being generalistic here. What happens if the owners of the sub-services are owned by different owners within the same entitiy and have a firewall between them ?

My point is that, I think John's message is that useful, exposed services should be as granular and business-like as possible because in essence - CreateCustomer is ONLY one small part of an entire business process and it is usually not an end to itself.

I hope I am making some sense here. :)

# Evan said on 21 August, 2007 11:30 PM

CRUD is ok if you are only using the call for data replication.  If the call invokes much functionality, tread carefully.  This gets really bad if you follow some of the older MS practices of using the entity as your serialization format (the data contract).

You end up with a User entity which is tied to some other entity (say, a set of BillingAddress objects).

When a client calls UpdateUser(user), what does your service do with the absence or presence of billing addresses?  Does it check them against the list in the database?  Does it remove or add addresses from the database?

We had a situation at work where a CRUDy interface was used for CreateUser().  At first it was simply data replication.  Later, it was used to both create a user and entroll the user in a specific program.  Program enrollment included emailing the user.  That worked great until a customer came along that wants to not email the customers.  Then things start to get messy.

We would have been better off to make things more explicit for the calling code.  Instead, we should have built an "EnrollUser" method to make things explicit.

Things get even worse when you have to orchestrate between 2 systems that use CRUDy services.  The stereotypical example is transferring money from one bank to another.

Rather than blather on, I'll just say that the following does a much better job of explaining why CRUD is an antipattern..

msdn2.microsoft.com/.../ms978509.aspx

Leave a Comment

(required) 
(required) 
(optional)
(required)