SOA update request granularity and nulls

108 Views Asked by At

We are striving towards an SOA enterprise...

Given three options for say updating member details how do we design the contract?

The business process is quite simple. The customer calls (or logs in themselves), and updates their personal details so that we have the latest details available. The customers employer can also supply members details (this will be in bulk - potentially 10s of 1000s at a time). This is so we can communicate with them correctly in the future. We have multiple back end systems.

The details are:

  • Phone numbers,
  • Addresses,
  • Email,
  • Name or company name,
  • Contact person,
  • Tax File Number,
  • Marital Status
  • Smoker status

As it stands now the business rules are: If a valid tax file number has already been supplied, you cannot supply it again. (can be overridden) If valid address details are present, the employer cannot update them, only supply them the first time.

Option 1: One operation, Member.UpdateDetails

  • Only one service to create and manage.
  • If business rules grow, this service could become less cohesive.
  • Has the problem of having to differentiate between specifying that something should be removed versus leaving it as is.
  • Single unit of work, single transaction.

Option 2: Break down into four operations: Member.UpdateContactDetails; Member.ProvideTaxFileNumber; Member.UpdateName; Member.UpdateDemographics

  • Potentially simplifies the single operation - spreads the complexity over the four operations.
  • Still has the problem of having to differentiate between specifying that something should be removed versus leaving it as is. For example what if I only wanted to specify smoker status without marital status.
  • Requires some deep analysis to figure out how to group these correctly - The cohesiveness depends on the business process.
  • More services to write and maintain.
  • Transactions become a concern - multiple transactions handled by the caller?

Option 3: Break down into smaller still: Member.UpdateAddress; Member.UpdateBusinessDetails; Member.UpdateContactNumbers; Member.UpdateContactPerson; Member.UpdateEmailAddress; Member.UpdateMailingAddress; Member.UpdatePhysicalAddress; etc.

  • Removes the issue of having to differentiate between specifying that something should be removed versus leaving it as is.
  • Business rules can evolve easily in whatever operation.
  • Loads of services to write and maintain.
  • Transactions become a concern - many transactions handled by the caller?
  • Start to look like property setters / CRUD - apparently a no go.

In option one or two, say the caller only wants to update the home email address - I cannot expect that the client complete the entire message - does the client leave all of the other tags out? What is the accepted, obvious, intuitive pattern to dealing with this problem?

If this IS indeed the pattern, then how does the client clear the field, or set it to null? In .NET, in the server code I cannot see an obvious way to distinguish between not supplied and null. Since it is not obvious, I expect that this is not an accepted pattern.

3

There are 3 best solutions below

0
On

Use a coarse grained api with fine grained arguments. If you don't want a field to be updated, pass along a descriptor saying that along with the data. Something like:

Result updateMember(Member member, List<String> fieldsToUpdate)

Having a fine grained API is basically death, because transport is often obscured.

If someone writes:

UpdateContactNumbers(...);
UpdateAddress(...);
UpdatePersonalDetails(...);

They have very likely just made a) 3 trips over the network along with b) 3 trips to the DB, concluding with c) 3 transactions on the DB. This doesn't count all of the marshalling and unmarshalling fun involved.

Which, of course, is insane.

Is a service API required to be remote? No, of course not, but many are, and minimally, many are transparent of transport (could be local or remote, you don't know).

So. Coarse API, fine grained arguments. Figure out what you want to do, in detail, and do it all in one go.

0
On

I would strongly suggest the second option, because it preserves the intention of the user making the change all the way through to the code that acts on it.

The first advantage is that it absolves you from answering the question of "how do I represent deletion in a DTO" - because now you would capture that fact as a DeleteContactNumber message, explicitly.

The second advantage is that you are absolved answering the question of "how do I add multiple addresses at once" - because you don't have to infer that someone added an address from the mutated DTO, you get an AddContactAddress message.

The third advantage is that you can do more interesting business analysis at the end of the day - because you know what the events that happen are, without having to do analysis of the DTO and infer that.

Finally, it becomes easy to add more information to the specific events: do you want to know why people are deleting their contact address?

Using a model of "fetch the data, mutate the data, save the data" is less lines of code, but it ultimately makes it harder to understand why things are done in your system - and that will eventually cost you.

4
On

Personally, I don't see too much wrong with having both the UpdateMember capability AND the simpler capabilities of UpdateAddress etc. Some may argue, but I think this will be perfectly acceptable.

'Intuitive' may be the better word to follow here - what feels right to you?

To me, UpdateMember would seem a definite candidate to include. If this service is being consumed by a UI, all the fields would likely already be populated by a GetMember call, so all the fields would be populated anyway with their original values. You could possibly use something similar even if it is not a UI. Then you can have UpdateAddress, Update PersonalDetails as well, for simpler, specialized circumstances.

What I don't like however is this idea of only having the UpdateMember capability and then leaving fields you don't want to change blank. I don't think many people use this pattern and I certainly wouldn't. As you say, how do you then set a field to null.