Domain Modelling in Angular Applications

There is a Ugandan story told of a traveller who went to a faraway land. Upon reaching his destination, he found great difficulty because he didn't know the local language of the place he found himself in. Despite this, he attempted exploring and getting to know more of the place.

He comes across a big house, and marveling at the house, asks a lady passing by, "Who owns this house?", to which she responds "Angazi". Later, while walking on the road he sees a beautiful car parked on the side of the road. He asks the man next to him who is also marveling at the car, "Who owns this car?", to which he responds "Angazi". A similar thing happens upon seeing a large business, and other wondrous features. Finally, a few weeks later, the traveller notices a big funeral procession, and asks, "Who passed away", and the usual response leaves him in absolute shock, thinking "such a great person has passed away!".

Now to anyone who has a basic understanding in Zulu, they know that angazi means 'I don't know'. Yet to the character in the story (as well as the person who told me the story), this was unknown, causing a misreading of the situations they found themselves in. Within the application code we write, communication between different elements such as modules, packages, namespaces and classes also has similar difficulties.

In Angular, information is passed between different components by the use of services, singleton objects, defined by classes, which are injected into the components upon creation. An advantage of having class definitions is that these services can be modeled in a way that shows business intent, something that domain driven design seeks to do.

Domain driven design, in my simple I-haven't-finished-the-book understanding of the term seeks to write the code using the terms of the roles and artifacts of the organisation it is being written for. This allows for a common understanding between developers, users, and other important stakeholders.

I'll use a hypothetical example to explain this further. Imagine a tribe, somewhere in deep Africa (because that is where tribes are). This tribe is tired of its leader, and so the tribal council decides to have a meeting to vote out the leader, as there are some who have lost confidence in her leadership. This tribe also has a ritual around the springtime, where they place meat on top of a metallic grill-like plate, which rests above burning coals. While this meat is burning, they stand and talk about the affairs of the tribe, such as the leadership. Now, because members of the tribal council fear being ostracised from invitation to partake in the ritual, they decide to make the vote of no confidence an anonymous vote. How would such a scenario be modeled?

Well, we could have a council member entity, as well as a vote value object, as defined by the following classes:

@Injectable()
export class CouncilMember {  
  private memberVote: Vote;

  castVote(vote) {
    this.memberVote = vote;
  }

  getVote() {
    return this.memberVote;
  }
}

export class Vote {  
  constuctor(private haveConfidence: boolean) {}

  haveConfidenceInChief() {
    return this.haveConfidence;
  }
}

The names of these objects are modeled after the context they are used in, and therefore when being used in the various Angular components, it makes it easier to understand their intent and purpose, making communication between developers working on the various components easier.

Imagine two components: a vote component and a confirm component which then make use of this:

@Component({
  templateUrl: './vote.component.html'
})
export class VoteComponent {  
  constructor(private councilMember: CouncilMember) {}

  voteAgaintNoConfidenceMotion() {
    this.councilMember.castVote(new Vote(true));
  }

  voteForNoConfidenceMotion() {
    this.councilMember.castVote(new Vote(false));
  }
}

The confirm component will then look like this:

@Component({
  templateUrl: './confirm.component.html'
})
export class ConfirmComponent implements OnInit {  
  confirmationText: string;

  constructor(private councilMember: CouncilMember) {}

  ngOnInit() {
    const hasConfidenceInChief = this.councilMember.getVote().haveConfidenceInChief();
    confirmationText = hasConfidenceInChief ? 
      'Are you sure you have confidence in the chief?' :
      'Are you sure you do not have confidence in the chief?'
  }
}

This example seems trivial, but as the code base grows, and more complexity is introduced, having a common, ubiquitous language between developers, users, analysts, etc. allows for a better understanding of the code base, making it easier to maintain and enhance the application.

Patrick Kayongo

I create and maintain software. Pan-African.

Johannesburg, South Africa