Complex projects and maintainability

“Keep it simple” is a good advice, but sometimes it is not so easy even in projects very small.

The problem with javascript (most than in other languages) is that even if you know that the project won’t be a “very small” one, it is not so obvious how to proceed in order to keep the code maintainable for you and the future heirs of your code (yap, a lot of times your wonderful code for the next guy is the crappy legacy code).

The single responsibility principle is not so easy to apply and sometime when you try to apply it you lose in readability.
How I manage it normally? With the angular Directives!

Let’s try playing with an example: the Dungeons & Dragons sheets (the Next version)

Sidwin the Sharp

The D&D sheets (or a RPG sheet in general) represents a good degree of complexity: there are a lot of information on the screen, some data have to be calculated in real-time and every part of the sheet can be updated independently from the other parts, or different parts of the sheet can be connected at the same level.
Most of the SPA with whom I deal day by day are less complex.

Thinking about the layout of the sheet I can imagine 4 big areas, a header and three columns.

Sidwin the Sharp-layout

In order to reuse and create sheet multiple times I’d like to have something like this

<sheets
   char-class=""
   char-name=""
   level=""
   race=""
   player-name=""
   experience="">

   <column>
   </column>

   <column>
   </column>

   <column>
   </column>
</sheets>

Dividing the sheet in components I can imagine something like this.

Sidwin the Sharp-directives

So I’d like to be able to write something like this

<sheets
   char-class=""
   char-name=""
   level=""
   race=""
   player-name=""
   experience="">

   <column>
     <statistics stats=""></statistics>

     <hr />

     <profix
        stats=""
        profix=""></profix>

     <other-profix
        profix=""></other-profix>
   </column>

   <column>
      <status-health
         armor-class=""
         initiative=""
         speed=""
         current-hp=""
         max-hp=""
         temp-hp=""></status-health>

      <hr />

      <attacks-spellcasting
          equipment=""
          spells=""></attacks-spellcasting>

      <hr />

      <equipment objects=""></equipment>
    </column>

    <column>
      <traits traits=""></traits>
    </column>
</sheets>

And passing the data required I’d like to achieve something like this

ded-next-web-sheet

The image above is the render provided by the current implementation.

There is a lot of advantages proceeding in this way:

1. Every directive is specialized and so very small.
2. You can move the directives between columns, duplicate it or in general reuse it without to copy a very complex and nested markup.
3. it is easy to test

Let’s to analyze the statistics directive as a sample:

<div class="statistics">
   <h3 class="clearfix"><span class="col-md-12">Statistics</span></h3>
   <table>
      <tr>
        <th class="col-md-4">Name</th>
        <th class="col-md-4">Value</th>
        <th class="col-md-4">Bonus</th>
      </tr>
      <tr data-ng-repeat="stat in stats track by $index">
         <td class="col-md-4">{{stat.name}}</td>
         <td class="col-md-4">{{stat.value}}</td>
         <td class="col-md-4">{{bonusByValue(stat.value)}}</td>
      </tr>
   </table>
</div>

the html is very simple, please notice the highlight row.
In my idea the stats array should be something like this:

stats: [
{ name: ‘strength’, value: 11, code: ‘str’ },
{ name: ‘dexterity’, value: 16, code: ‘dex’ },
{ name: ‘constitution’, value: 16, code: ‘con’ },
{ name: ‘intelligence’, value: 14, code:’int’ },
{ name: ‘wisdom’, value: 11, code:’wis’ },
{ name: ‘charisma’, value: 18, code:’cha’ }
]

The bonus given by the value is something defined by the rules so if the rules change we need to recalculate it following the new ones.
This is why I didn’t put the bonus in the stats and this is why I don’t think should be responsability of the sheet to calculate it.
So this is how I managed my statisticsDirective.js

angular.module('DeDSheets.directives')
.directive('statistics', ['statsService'
    , function (statsService) {
    return {
        restrict: 'E',
        replace: true,
        scope: {
            stats: '=',
        },
        templateUrl: '/AppJs/DeDSheets/app/views/directives/statisticsDirective.html',
        link: function(scope) {
            scope.bonusByValue = function(value) {
                return statsService.bonusByValue(value);
            };
        }
F
    };
}]);

I gave it a dependency to a service able to calculate the bonus following the rule.
To simplify I put the rules inside the service itself, but in a real scenario is quite possible that the service will use another service to get from the db/file-system the rules.
The directive in this way is agnostic and if you want you can test separately the pure directive (mocking the service) and the service itself.

Working in this way I found a very good balance between readability, scalability and I found a good way to implement the Single Responsability Principle.

Here how it is organized this solution

solution-ded

It is not the case, but in general every time I have to use a ng-repeat, if the content is complex can be a good candidate to become a directive even to just simplify the readability of the markup.

If you want to take a look to the whole project and the other directives, you can go on my github.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s