November 22, 2015

A naming convention for domain-driven programming



In this blog post I’d like to present a quite simple naming convention for identifiers but which is enormously helpful to build true domain-driven, object-oriented structures. Whenever I see this convention violated in practice, it usually comes with poor, procedural design and code which is hard to read.

The naming convention I present here proposes that every identifier’s name is domain-dependent, “domain” referring to its context. I am certain the rigorous application of this naming convention increases code readability, maintainability and accords especially well with domain-driven (DD) design and object orientation. I will here present some Java code examples, but I think it’s as well applicable to any object-oriented or even procedural programming language.

Identifiers must be unique. The compiler enforces that.

However, this DD naming convention proposes that an identifier should be unique only within its scope, its domain. Let’s start with an example.

Method variables

Let’s assume callPersonSaveService(…) is a method in a strongly service oriented interface:
public class PersonClient {
    private PersonService personService;

    public String callPersonSaveService(Person person) {
        PersonSaveRequest saveRequest = new PersonSaveRequest();
        saveRequest.setPerson(person);
        PersonSaveResponse saveResponse = personService.savePerson(saveRequest);
        return saveResponse.getMessage();
    }
}
Because this method is part of a PersonClient, we can already assume that it handles Persons, right? There’s no need to repeats this information in the variable names. Also, from the method name we can infer that it handles a “save” operation, so there’s no need to repeat that information in variable naming either.

Actually, repeating any information is a violation of the DRY (don’t repeat yourself) principle. Now consider this DRYed variation of above method:
public String callSaveService(Person model) {
    PersonSaveRequest request = new PersonSaveRequest();
    request.setPerson(model);
    PersonSaveResponse response = personService.savePerson(request);
    return response.getMessage();
}
What have we gained? First of all, readability is increased. Short, more concise variable names are easier to read. Also, I can immediately identify the main actors: Here’s the model, there’s the request, there’s the response. I know from the context, from the business domain, what kind of model etc. is involved in this method. You can say that the context actually is part of the “fully-qualified” name of an identifier: com.mycompany.PersonClient#callSaveService.model.

Okay, but what is the actual benefit? Things get more rewarding as soon as you have to add functionality. Imagine building an equivalent callPersonDeleteService(…) method.

As a very KISS (but not DRY) approach, let’s say we do copy-paste coding.
public String callDeleteService(Person model) {
    PersonDeleteRequest request = new PersonDeleteRequest();
    request.setPerson(model);
    PersonDeleteResponse response = personService.deletePerson(request);
    return response.getMessage();
}
Obviously, it’s much easier to copy existing code because you don’t have to change variable names. Less changes mean less work, and less potential errors.

But now we can do even better. Applying domain-specific variable naming helps us identifying similarities and hence potential abstractions. We can clearly see that both these methods make a model-based request and return a String message from a response. We can use this information to build a very DRY (but admittedly not KISS) solution by using a common interface to build a small abstraction layer and reduce the need for copy-paste-coding:
private <T extends PersonRequest> String callService(Person model, T request, 
    Function<T, PersonResponse> serviceCall) {
    request.setPerson(model);
    return serviceCall.apply(request).getMessage();
}

public String callSaveServiceKiss(Person model) {
    return callService(model, new PersonSaveRequest(), personService::savePerson);
}

public String callDeleteServiceKiss(Person model) {
    return callService(model, new PersonDeleteRequest(), personService::deletePerson);
}
Now, both PersonSaveRequest and PersonDeleteRequest inherit from a common superclass PersonRequest, and ditto for the response classes.

Of course, this is ridiculously overengineered for this simple case, and this code only stays readable because we use Java 8 method references here.

Still, this example illustrates how applying domain-driven thinking on variable naming helps us identify abstractions, which is even more important when thinking in bigger scale structures.

Class members

Now consider this class representing a CRUD controller for a Person entity, as it is e.g. typically used to serve as a backing-bean for JSF UI Views:
public class PersonController {
    private PersonService personService;
    private List<Person> allPersons;
    private Person selectedPerson;
    
    public void initController() {
        setAllPersons(personService.findAllPersons());
    }
    
    public void saveSelectedPerson() {
        if (getSelectedPerson().getId() == null) {
            personService.insertPerson(getSelectedPerson());
        }
        else {
            personService.updatePerson(getSelectedPerson());
        }
    }

    public List<Person> getAllPersons() {
        return allPersons;
    }

    public void setAllPersons(List<Person> allPersons) {
        this.allPersons = allPersons;
    }

    public Person getSelectedPerson() {
        return selectedPerson;
    }

    public void setSelectedPerson(Person selectedPerson) {
        this.selectedPerson = selectedPerson;
    }
}
Do you see the person identifier cluttered all over the code? Now imagine we want to add another controller with the same CRUD functionality but for a Reservation entity:
public class ReservationController {
    private ReservationService reservationService;
    private List<Reservation> allReservations;
    private Reservation selectedReservation;
    
    public void initController() {
        setAllReservations(reservationService.findAllReservations());
    }
    
    public void saveSelectedReservation() {
        if (getSelectedReservation().getId() == null) {
            reservationService.insertReservation(getSelectedReservation());
        }
        else {
            reservationService.updateReservation(getSelectedReservation());
        }
    }

    public List<Reservation> getAllReservations() {
        return allReservations;
    }

    public void setAllReservations(List<Reservation> allReservations) {
        this.allReservations = allReservations;
    }

    public Reservation getSelectedReservation() {
        return selectedReservation;
    }

    public void setSelectedReservation(Reservation selectedReservation) {
        this.selectedReservation = selectedReservation;
    }
}
For a potential reader of this source code (yes, this would be your fellow developer), at first glance, these two classes apparently have hardly anything in common; whilst in reality, of course, they are actually identical except that they serve different entity types.

We immediately realize this as we apply domain-specific naming e.g. to the PersonController and the PersonService. It then becomes apparent that we can actually build a big unified interface for both Controller and Service functionality, respectively, by using Java’s abstraction facilities, namely inheritance and generics. We can build the functionality once, in a super class, and building an implementation is a one-liner:
public abstract class Controller<T extends Entity> {
    private Service<T> service;
    private List<T> entities;
    private T selectedEntity;
    
    public void initController() {
        setEntities(service.findAll());
    }
    
    public void saveSelectedEntity() {
        if (getSelectedEntity().getId() == null) {
            service.insert(getSelectedEntity());
        }
        else {
            service.update(getSelectedEntity());
        }
    }

    public List<T> getEntities() {
        return entities;
    }

    public void setEntities(List<T> entities) {
        this.entities = entities;
    }

    public T getSelectedEntity() {
        return selectedEntity;
    }

    public void setSelectedEntity(T selectedEntity) {
        this.selectedEntity = selectedEntity;
    }
}

public class PersonController extends Controller<Person> {
    // empty
}
As far as the abstract Controller class is concerned: Again, it is clear from the context, that the PersonController will deal with person entities. There’s no need to repeat this information.

As you can see, just be using sensible variable naming, we have identified a way to build a simple yet powerful CRUD abstraction over all layers. (If you’re interested in seeing a true, complete CRUD abstraction for JSF in action, please take a look at my recent CrudFaces implementation.)

But applicability of these best practices don’t stop here.

HTML / Facelets markup

Context-sensitive naming policy, as described above, should also be applied to markup source code artifacts, especially if they do not offer the same degree of abstraction as e.g. the Java language does, thus de facto typically leading to error-prone copy-paste coding. In a typical Java EE / JSF stack, this is especially true for XHTML / Facelets files.

Consider this example:
<h:body>
  <h:dataTable value="#{personController.models}" var="person">
    <h:column>
      <f:facet name="header">
        <h:outputText value="Id"/>
      </f:facet>
      <h:outputText value="#{person.id}"/>
    </h:column>
    <h:column>
      <f:facet name="header">
        <h:outputText value="Name"/>
      </f:facet>
      <h:outputText value="#{person.name}"/>
    </h:column>
  </h:dataTable>
  <h:commandButton value="Back" action="#{personController.back}">
</h:body>
Here, we define a simple table rendering for each person item in personController.models its id and name.

The iteration variable is named person here although this violates the context-dependent naming policy: It is clear from the context of the iteration (over personController.models) that the element is of a person type. This again makes the code hard to read, and hard and inconsistent for reuse (both by copy-pasting and by refactoring into a higher abstraction). Things of course are naturally worse in XHTML code due to the typically limited IDE tool support.

Now consider this improved example code:
<h:head>
  <ui:param name="controller" value="#{personController}"/>
</h:head>
<h:body>
  <h:dataTable value="#{controller.models}" var="item">
    <h:column>
      <f:facet name="header">
        <h:outputText value="Id"/>
      </f:facet>
      <h:outputText value="#{item.id}"/>
    </h:column>
    <h:column>
      <f:facet name="header">
        <h:outputText value="Name"/>
      </f:facet>
      <h:outputText value="#{item.name}"/>
    </h:column>
  </h:dataTable>
  <h:commandButton value="Back" action="#{controller.back}">
</h:body>
We changed two things here:
  • First, as described above, the iteration variable is now neutrally named “item”. Its true nature is implicitly shown by its context.
  • We also created an alias for the controller, thus enabling the use of the neutral controller variable to refer to the controller. This is a best practice especially when your MVC architecture implies a 1 : 1 relationship between view and controller: Here, the view becomes the context within which the controller is defined. In the person view, there naturally lies the person controller. It’s thus again harmful to repeat that information. This of course is even more severe if the controller is referred to many times throughout the view.
This code is now consistently set up and ready for reuse, either to facilitate easy copy-pasting or, preferably, using abstraction techniques such as composite components.

Conclusion

I personally believe that opting for context-dependent identifier naming is a vital step in making source code more modular, and identifying code duplications and abstraction potential. As professional engineers, it’s our job to build abstractions, and to strive for DRY (without breaking KISS). This is what ultimately leads to clean, S.O.L.I.D. code.

I decided to dedicate a whole blog post to this topic because I’ve seen even experienced developers struggling to recognize the value of this simple yet powerful naming convention.

So, do you agree with what I wrote in this article? Please feel free to raise your own opinion or share your experience with this matter in the comments section below.

No comments:

Post a Comment