Let's say you have some kind of data structure, which is persisted in some kind of database. For simplicity, let's call this data structure Person
. You are now tasked to design a CRUD API, which allows other applications to create, read, update and delete Person
s. For simplicity, let's assume that this API is accessed via some kind of web service.
For the C, R and D parts of CRUD, the design is simple. I'll use C#-like functional notation - the implementation could be SOAP, REST/JSON, or something else:
class Person {
string Name;
DateTime? DateOfBirth;
...
}
Identifier CreatePerson(Person);
Person GetPerson(Identifier);
void DeletePerson(Identifier);
What about update? The natural thing to do would be
void UpdatePerson(Identifier, Person);
but how would you specify which fields of Person
to update?
Solutions that I could come up with:
You could always require a complete Person to be passed, i.e. the client would do something like this to update the date of birth:
p = GetPerson(id); p.DateOfBirth = ...; UpdatePerson(id, p);
However, that would require some sort of transactional consistency or locking between the Get and the Update; otherwise, you could overwrite some other change done in parallel by some other client. This would make the API much more complicated. In addition, it's error prone, since the following pseudo-code (assuming a client language with JSON support)
UpdatePerson(id, { "DateOfBirth": "2015-01-01" });
-- which looks correct -- would not only change DateOfBirth but also reset all other fields to null.
You could ignore all fields that are
null
. However, how would you then make a difference between not changingDateOfBirth
and deliberately changing it to null?Change the signature to
void UpdatePerson(Identifier, Person, ListOfFieldNamesToUpdate)
.Change the signature to
void UpdatePerson(Identifier, ListOfFieldValuePairs)
.Use some feature of the transmission protocol: For example, you could ignore all fields not contained in the JSON representation of the Person. However, that usually requires parsing the JSON yourself and not being able to use the built-in features of your library (e.g. WCF).
None of the solutions seems really elegant to me. Surely, this is a common problem, so what is the best-practice solution used by everybody?
Person
instances that still are not persisted, and in the case the identifier is decided as part of the persistence mechanism, just leave it to null. As for the answer, JPA uses a version number; if you read version 23, when you update the item if the version in DB is 24 the write fails.PUT
andPATCH
methods. When usingPATCH
, only send keys should be replaced, withPUT
the whole object is replaced.