Saturday, April 23, 2016

Component based architecture

Once angular 1.5 got released and components seems to be the future of Frond-end development, I started looking to Component Based Architecture. A good well defined component is easy to reuse and the flow of the data is very clear. That is way it is also easily testable. Next to these dumb/presentational components we also have some smart components who are aware of services/routing. These smart components control the dumb/presentational components. They are responsible to act on the changes made in the dumb/presentational components and delegate these changes back.
Further on in the post I will only speak of dumb components, but the same applies on presentational components.
The schema gives you an overflow of how every thing works together. The redlines show the flow of the data. The blue line are the change events emitted by the dumb components. The smart component is just responsible for retrieving and storing the data and keep everything consistent. This schema I found on a very good blog post about Refactoring angular apps to components.
data_down_actions_up

Smart Components

We call the Smart Components smart, because they have a notion where they can retrieve and store the data. They are able to communicate with services and provide the dumb components of their data. They also handle the actions/events raised by the dumb components and act upon them. This can be storing the data to a service or updating a local data context, but also providing dumb components which have auto complete fields with their items.
In most cases smart components won’t have a lot of markup. Mostly the mark-up will be present in the dumb components, but it’s not wrong to have some in the smart component. (In most cases this will be markup to structure your markup or parts that aren’t or won’t be reusable.)
By making your smart component the single source of truth you can easier manage the behavior of your dumb components. I’ll explain this by a small example:
We have a list of translations. Every translation has a language which has to be unique inside the list. So when adding a new translation we want all the languages who are already present filtered out of the language choices. If we would retrieve the list of languages inside the adding component, then the adding would need to have a notion of either the list of translations or the component managing the translations list. We don’t want any of both situations because this would decrease the reuse of the adding component. By retrieving this data from the smart component, we can work around this. Because the smart component knows the translations list, it can easily filter out the existing languages before passing them to the adding component.
Just because the fact that smart components are your single source of truth makes them hard even impossible to reuse. It is more likely that you will reuse your dumb components with other services then vice versa. This is also the reason why you will have little smart components and many dumb components inside your application.

Dumb or Presentational Components

As you could have guessed the dumb components are the opposite of the smart component. Instead of retrieving data, the data is passed to them and if they make changes to the data, these changes are exposed using action/events. As you can see these components have a well-defined interface with all their in- and outputs. This makes the purpose of your component very clear and will help you to improve your understanding of your application even better for you and your team.
The  goal of a dumb component is that you can easily reuse it inside your application. That is why it’s good to keep your dumb small. This will increase the chance you will be able to reuse it. This should also be a trigger to help you decided whether you should split your code up into a component or not.
Another way to help you decide how to split up into dumb components is by keeping the design principle “Separation of concerns” in mind. Every concern can be a potential component. My opinion on this is to be pragmatic on this. If your not planning on reusing some parts then don’t just split them up jus because they would be great components. Keep them in a single component, and if the day comes that you can extract some reusable components, you can still refractor them.
Because dumb components are small and well defined they are also very easy to test. You don’t need to mock anything because you can just pass the data directly to your component.

Conclusion

By implementing this way of working into my current project, I was able to extract a lot of reusable components that in other cases would have been copied with minor adjustments. Now they live as separate components that can be easily maintained.
It also made my code more readable. Instead of big chunks of codes in a single controller, I now have my well-defined components implementing their own concerns. By hiding the implementations details inside the components my smart component is very clean in code but still very clear and readable.
In my next post I will handle on how I implemented this approach inside an angular 1.5 application.

Wednesday, April 13, 2016

Project setup in Angular 1.x

Since 6 months now I have been working on an Angular 1.x project. In the start I have been struggling with the setup of the project, but I found a good way to start with the Angular 1 Style guide of John Papa. Combined with an module loader I optimized this setup.

Below you will find an example how to structure your application. The examples I will provide will use es6, but the concept behind it can easily be used with module loaders like commonJS and requireJS. In these cases just the imports of the files and the export of the file it self is handle a bit different.

Structure

/app
   /feature
       /subFeature
            component.js
            directive.js
            constants.js
            index.js
            subFeature.module.js
       controller.js
       component.js
       service.js
       feature.module.js
       feature.routes.js
       index.js
   myApp.js
   myApp.module.js
   index.js

As you can see in every folder structure I follow the same pattern. I have the following files:

  • index.js
    • The entry point of the folder
  • *.module.js
    • The definition of the angular module
  • *.routes.js
    • The routing definitions
  • other files
    • Controllers
    • Directives
    • Components
    • Services
    • constants

    • ….

*.module.js

The *.module.js file is used to define the angular module. In here we import angular and all the modules on which the module should depend on. If we are adding internal dependencies, we can do this by importing the index file of that module. This index file will init the module and expose the name of the module. (example: feature.module.js)

The module it self is exported so we can use it when we want to add services, controllers, components, … to it.

subFeature.module.js

   1: import angular from "angular";
   2: // import external dependencies necessary in the module
   3:  
   4: export default angular.module("myApp.feature.subFeature", []);

feature.module.js

   1: import angular from "angular";
   2: import subFeatureModule from "./subFeature/index";
   3: // import external dependencies necessary in the module.
   4:  
   5: export default angular.module("myApp.feature", [subFeatureModule]);

myApp.module.js

   1: import angular from "angular";
   2: import featureModule from "./feature/index";
   3: import "angular-route";
   4: // import external dependencies necessary in the module.
   5:  
   6: export default angular.module("myApp", [featureModule, "ngRoute"]);

*.routes.js

The *.routers.js is used to define the routing in your app for the feature. If you are using the new component router, this isn’t necessary because the routes will be defined inside the components.

   1: import FeatureModule from "./feature.module";
   2:  
   3: FeatureModule.config(function($routeProvider, $locationProvider) {
   4:   $routeProvider
   5:   .when('/Feature', {
   6:     templateUrl: 'controller.html',
   7:     controller: 'Controller'
   8:   });
   9: });

index.js

The index.js file is the entry file for the folder. In here we import all the components, controllers, services, … we want to add to the module. We also import the *.module.js file so we can expose the module name. This way we can easily use it to add the module as a dependency in another module.

The name of this file is irrelevant, you can also use something like init. The reason I use index.js is because this is the default entry point of a folder in webpack.

subfeature/index.js

   1: import "./component";
   2: import "./constant";
   3: import "./directive"
   4: import SubFeatureModule from "./subFeature.module";
   5:  
   6: export default SubFeatureModule.name;

feature/index.js

   1: import "./component"
   2: import "./controller";
   3: import "./service";
   4: import "./feature.routes";
   5: import featureModule from "./feature.module";
   6:  
   7: export default featureModule.name;

app/index.js

   1: import MyAppModule from "./myApp.module";
   2:  
   3: export default MyAppModule.name;

Other files

All the other files like (services, controllers, directives, constants, …) will import the *.module.js file. Every thing else necessary to define the service, controller, directive, … will also be present in this file. This way everything is present on a single place.

subFeature/component.js

   1: import SubFeatureModule from "./subFeature.module";
   2: // other imports
   3:  
   4: ComponentController.$inject = [];
   5:  
   6: SubFeatureModule.component("component", {
   7:     template: "<div></div>",
   8:     controller: ComponentController,
   9: })
  10:  
  11: function ComponentController(){}

subFeature/constant.js

   1: import SubFeatureModule from "./subFeature.module";
   2: // other imports
   3:  
   4: SubFeatureModule.constant("constant", {
   5:     "key": value
   6: })

Conclusion

By defining the component in the *.module.js file we have a single point where we can find the definition of the module. By exposing the module we also create a single point where we keep the name of the module. You no longer need to use the name of the module if you want to add a service/controller/… to it. This eliminates typo’s in the module names.

The index.js provides a single entry point for the module. All the services/controllers/… you want to add to the module should be added in here. Also we import the *.module.js file. This gives us the ability to expose the name of the module. These names can then be use to be added as dependency without having to know it string value. Again this eliminates typo’s in the module names.

In all the other files the definition and resolving of the dependencies can be put together. This downsizes the chance on errors and dependency mismatches