I find the old object-oriented design technique of CRC Cards to be useful when defining service designs. CRC is short for "Class, Responsibilities, Collaborators." It's a way to define what behavior belongs inside a service and what behavior it should delegate to other services.

Simulating a system via CRC is a good exercise for a group. Each person takes a CRC card and plays the role of that service. A request starts from outside the services and enters with one person. They can do only those things written down as their "responsibilities." For anything else, they must send a message to someone else.

Personifying and role-playing really helps identify gaps in service design. You'll find gaps where data or entire services are needed but don't exist.

Tell, Don't Ask

The more services you have, the more operational complexity you take on. Some patterns of service design seem to encourage high coupling and a high fan-in to a small number of critical services. (Usually, these are "entity" services… i.e., CRUDdy REST over a database table.)

Instead, I find it better to tell services what you want them to do. Don't ask them for information, make a decision, then change some state.

Organizing around Tell, Don't Ask leads you to design services around behavior instead of data. You'll probably find you denormalize your data to make T,DA work. That's OK. The runtime benefit of cleaner coupling will be worth it.

Data Flow Diagrams

If you ask someone who isn't trained in UML to draw a system's architecture, they will often draw something close to a Data Flow Diagram. This diagram shows data repositories and the transformation processes that populate them. DFDs are a very helpful tool because they force you to ask a few really key questions:

  1. Where did that information come from?
  2. How did it get there?
  3. Who updates it?
  4. Who uses the data we produce?

In particular, answering that last question forces you to think about whether you're producing the right data for the downstream consumer.