MCUs 101

Intro to MCUs

LianaMobile uses a modularised approach to application development, which can be seen in extensive use of packages, simply called MCUs, that are shared between projects. We develop most of our packages ourself and all of these packages are located inside a centralised repository, called monorepo. Here we will go through what are MCUs, how to use them and how to develop them. Last part is dedicated to documenting instructions.

Read these first

Structure of a monorepo

All of the packages (MCUs) inside monorepo are divided into three categories:

  1. Components - Smaller, “bumb” UI controls. Components do not edit data or contain any application logic.
  2. Modules - Bigger, independent part of UI. Can subscribe and process data can contain many components.
  3. Utilities - Any other MCU that do not contain any UI.

Mono vs mono-meteor: These are our two monorepos, difference being that mono-meteor contains packages that are somehow dependent on meteor packages and mono contains meteor-independent packages that are published also to npm.

Mono-meteor structure

We may refine the directory structure in the future, but currently it goes as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mono-meteor
├── components
│ ├── component-avatar
│ ├── component-block
│ ├── component-button
│ └── ...
├── modules
│ ├── module-data-list
│ ├── module-easy-charts
│ ├── module-feedback
│ └── ...
└── utilities
├── utility-accounts-base
├── utility-back-levels
├── utility-easy-log-transformer
└── ...

Development phases of a packages

Each package in monorepo is currently in one of the following three phases. The current phase can be found from the package documentation:

Phase 1: Plugin not for universal use, used in one project
Phase 2: Plugin fit for universal use, used in one project
Phase 3: Plugin in universal use, used in multiple projects

Phase 1

When a package is first created, it is usually added to monorepo as a phase 1 MCU (or in certain situations phase 2 if it is ready for universal use). Phase 1 MCUs may contain application specific code, so they are not ready to be used in other applications as is. Still even phase 1 MCUs should be designed in a way that it is easy to modify to phase 2.

Phase 2

Phase 2 package does not contain any application specific logic. It is still used in one project, but ready to be used in other projects (sometimes with small modifications).

Phase 3

Phase 3 packages are already used in multiple projects. They may (read will) still be developed further.

Building apps with MCUs

Our apps are build entirely from modules. This is very straightforward because of the way our apps are structured.

App structure:

See app core specifications in:

https://drive.google.com/open?id=0Bzu3LDj7cBCYMXJfOTBOUDNSMFE
https://drive.google.com/open?id=0Bzu3LDj7cBCYaUpjNUxkWHVNNWs

Using MCUs

Finding MCUs

Using Github find

For a quick and dirty way of finding MCUs you can use the the search bar inside the monorepo in Github. This may give mixed results however.

Docs

A better way of finding the MCU for your needs are the docs (coming up..). More info about how to document a package further at the bottom.

Adding to project

Adding a mono-meteor package

A mono-meteor package is basically an Atmosphere package hosted in a private Github repo. These packages can be installed to your project with mgp. Add the package to git-packages.json at the root of your project in following way:

1
2
3
4
5
6
7
8
9
10
11
12
{
"lianamobie:package-name": {
"git": "git@github.com:LianaMobile/mono-meteor.git",
"path": "folder-in-mono-meteor",
"version": "commithashortag"
},
"lianamobie:other-package-name": {
"git": "git@github.com:LianaMobile/mono-meteor.git",
"path": "folder-in-mono-meteor",
"version": "commithashortag"
}
}

The commithashortag being the hash for the monorepo HEAD where the package should be downloaded from. This can be used to install different versions of the package. For example:

1
2
3
4
5
6
7
8
9
10
11
12
{
"lianamobile:module-register": {
"git": "git@github.com:LianaMobile/mono-meteor.git",
"path": "module-register",
"version": "eb38e83"
},
"lianamobile:component-checkbox": {
"git": "git@github.com:LianaMobile/mono-meteor.git",
"path": "component-checkbox",
"version": "92e20e0"
}
}

After the package is added to git-packages.json, run the following command in the root of the project:

mgp --https

After this run meteor add

meteor add lianamobile:name-of-the-new-package

Adding a mono package

Packages in mono repository are built as npm packages and can be installed by using npm. Run the following command at the root of your project:

meteor npm install --save @liana/package-name

For example

meteor npm install --save @liana/utility-customer-schema

Creating and editing MCUs

Working with Github

More info about working with Github in Github Workflow.

Versioning

Every package must have a version number and every time before some changes to the package are pushed or published, the version number must be bumped. We are using so called semantic versioning. This is an excerpt from the website:

Given a version number MAJOR.MINOR.PATCH, increment the:

  1. MAJOR version when you make incompatible API changes,
  2. MINOR version when you add functionality in a backwards-compatible manner, and
  3. PATCH version when you make backwards-compatible bug fixes.

So if the current version is 1.2.3, and we make a patch bug fix, we change the number to 1.2.4. In case of a minor functionality change 1.3.0. If you are unsure about how to bump the version number, ask someone from #core channel at Slack.

Workflow

A new MCU is created whenever a new feature is needed in an app but no module for this purpose does not exist yet. Modules are updated whenever needed, for example when adding them to a new app.

Case 1: An module is needed in an app that does not exist yet

  1. App developer creates an issue in mono-meteor
  2. App developer creates and develops a new package with all needed code and documentation and adds a pull request
  3. Senior developer checks that the module does what it is supposed to do and that the code and documentation is in good condition
  4. After approving the pull request, module is plugged in to the app

Case 2: A new feature is needed in an existing module, so the package needs some updating

  1. App developer creates an issue in mono-meteor,
  2. App developer modifies the module package however needed and adds a pull request
  3. Senior developer checks that the module does what it is supposed to do and that the code and documentation is in good condition
  4. After approving the pull request, module is updated inside the app

MCU requirements

Here are some things that should be kept in mind when developing an MCU. The point of these requirements is to ensure that MCUs are easy to use and modify.

Code should be easy to read

This includes things like commenting the more complex parts and not creating crazy one line ternaries with loops or something. If someone wants to change some behaviour in the package, it should be clear enough what part is responsible for this behaviour and how to change it.

Good documentation

Documentation should explain how to configure and use the MCU for someone who has not used it before and does not know what it is for.

Code should be easy to modify

Functions, parameters etc. should be designed in a a way that they are easy to modify and add to later. MCUs will be used in different projects than what they are originally created for that may have need requirements that cannot be anticipated fully.

Giving all customization in parameters

Styling, messages etc. should be given in parameters. For example:

1
2
3
{{> someModule
css="css-class1 css-class2"
}}

Preparing for offline support

All our apps will not be needing offline support, so it is not necessarily required to add it when the package is created. However, it should be remembered that offline usage may be needed in the future so preparing for this may avoid a lot of gray hairs in the future. This means avoiding unnecessary server calls, designing a way that external assets can be cached etc.

Structure (modules and utilities)

These guidelines apply mostly to modules and utilities. All modules utilities should try to stick to the structure. This enables easy usage and future development.

General structure

Boilerplate follows the general structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
example-package
├── client
│ ├── main-client.js
│ ├── some-template.html
│ ├── some-template.css
│ └── ...
├── common
│ ├── some-library.js
│ ├── collections.js
│ └── ...
├── server
│ ├── main-server.js
│ ├── server-stuff.js
│ └── ...
├── tests
│ ├── server-tests.js
│ └── ...
├── package.js
└── README.md

As can be seen: client and server code is split into their own folders. main-client.js exports all code that is used in client and main-server.js exports code to server. Folder common includes common libraries etc.

Although the module exports only one class, it is not required to fit all code inside the class, but it should be split logically into functions and from there on into files. All functions and files should include comments explaining what their purpose is.

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Function that checks if given String length is 0.
* @param String someString any String.
* @returns boolean true if given String length 0, otherwise false.
*/
const someFunction = function(someString) {
if(someString.length === 0){
return true;
}else{
return false;
}
};

Initializing

…coming soon..

Exports

Utilities should export one class that has the main functionalities (instead of exporting individual functions).

Prefer this:

1
2
3
4
5
6
7
8
9
10
class printLetter {
init(letter) {
this.letter = letter;
}
print() {
console.log(this.letter);
}
}
export default testExample;

Avoid this:

1
2
3
4
5
6
7
8
9
10
var letter;
init(letter) {
letter = letter;
}
print() {
console.log(letter);
}
export {init, print};

Same with modules, they should only export one main template.

Prefer this:

1
2
3
4
5
6
7
8
9
<template name="exampleModule">
<div id="header">
Hello world!
</div>
<div id="image">
<img src="someImage.jpg">
</div>
</template>

Avoid this:

1
2
3
4
5
6
7
8
9
10
11
<template name="exampleModuleHeader">
<div id="header">
Hello world!
</div>
</template>
<template name="exampleModuleImage">
<div id="image">
<img src="someImage.jpg">
</div>
</template>

Documentation

Packages should include a README.md file that contains at least the following points:

1. Phase

What phase is the MCU in. More info about phases

2. General purpose

What is this MCU used for? In what situation and what way? Images?

3. How to use

All relevant info someone needs to install this in to an app and start using.

3.1 Any initialization

3.2 All exports and parameters

3.3 Other info

4. TODO

Stuff that future developers of this package should know. What was left undone when developing and how to improve it otherwise.

Boilerplate

There is a module boilerplate in github.

Publishing MCUs

To mono-meteor

Like mentioned earlier, packages in mono-meteor are Atmosphere packages hosted in a private Github repository. When adding a new package, create it under appropriate folder either in components, modules or utilities. More instructions about working with mono-meteor in mono-meteor readme.

To mono

Mono packages are hosted in a private npm account (info in 1Password). If adding a new MCU, you can create an npm package to mono, push it, and then publish to npm. More instructions about working with packages in mono in mono readme.

Creating Project Schemas

First copy latest version of mono repo to your computer.

1
git clone https://github.com/LianaMobile/mono

Or if you have it already, make sure you don’t have any
changes by running git status and after that run:

1
2
3
cd mono/
git checkout master
git pull --rebase

Go to packages folder inside mono repository:

1
cd mono/packages

Create new schema package by using utility-customer-schema as boilerplate (remember to change [YOURCUSTOMERNAME] e.g. utility-nokia-schema):

1
cp -r utility-customer-schema utility-[YOURCUSTOMERNAME]-schema

After that we need to change package name: from package.json. You can change the name in editor or via command line with following code (remember to change [YOURCUSTOMERNAME] e.g. utility-nokia-schema):

1
sed -i '' 's|@liana/utility-customer-schema|@liana/utility-[YOURCUSTOMERNAME]-schema|' package.json

Now you can start adding your own schemas and editing existing schemas for your use case.

Publishing New Schema

  1. install dependencies by running: yarn
  2. build your package by running: yarn build
  3. publish your package by running: yarn publish (asks new version number)
  4. commit your changes and push to mono repository
Edit on GitHub