# Add a new module chompkamek
# Modules
Unless you are an Admin, you will only have access to a set of functionalities. Either way, medium/big sized applications can always be broken in modules. Think in modules as big features, normally accessed throught a navigation bar.
This separation of concerns allows to determine easly what logic must or not be load at any point. If a user logins in the application and never goes to the Roles module, any code like api calls, images, components, store and even translations won't be loaded for the Roles module.
If the user's browser supports service workers, after getting the code needed for the first load it will fetch the remaining files allowing a smooth navigation afterwards.
Modules live inside the src/modules
folder and the router to allow to access a module inside the router
folder.
.
├─ config
├─ public
├─ src
│ ├─ api
│ ├─ assets
│ ├─ components
│ ├─ locales
│ ├─ modules # modules live inside this folder
│ └─ ...
├─ plugins
├─ router # routes to access modules are here
├─ store
├─ utils
├─ views
├─ tests
└─ ...
# Adding a new module
Let's imagine we want to create a new module called "countries". What would be the steps?
- Create a
countries
folder for the new module inside thesrc/modules
folder; - Inside the new module we usually have the following structure:
modules
└─ countries
├─ _api # only api calls for this module
├─ _components # components that are only used in this module
├─ _store # countries vuex store
├─ ViewCountries.vue
├─ ViewCountriesList.vue
└─ ... # Views/pages for the countries module
- Configure countries routes creating a
countries.js
file on therouter
folder:
router
├─ index.js # countries.js must be included inside this file
├─ countries.js # router configurations for the new module
└─ ...
// countries.js
import { loadView } from './utils'
const MODULE = 'countries' // created module must match this name
export default [
{
path: '/countries',
component: loadView('ViewCountries', MODULE),
children: [
{
path: '',
name: 'countries',
component: loadView('ViewCountriesList', MODULE),
meta: {
navigationDrawer: true, // true|false
module: MODULE
}
}
]
}
]
At this point we must be able to access ViewCountriesList page accessing directly /countries
url.
- Map the new module to an icon and text
WARNING
- Specify where
- icons link
{
icon: 'house',
routeName: 'countries',
text: 'Countries' // must be translated
}
Now, we must have the new module available at the navigation drawer. However, this module can not do much at this point. Go to the next section to see how to properly request information from the API.
# API
# Modules API requests
As described in the previous section inside the countries
module we have an _api
folder where the api requests for that specific module will be handled. It strucure must be:
modules
└─ countries
├─ _api
│ ├─ index.js # where api calls are made
│ ├─ requestMapper.js # mapper to create the body to send to the API
│ └─ responseMapper.js # mapper API responses to the frontend structure
└─ ...
# Generic API requests
Generic requests like login, get available features or get CMS blocks must be in the src/api
folder.
Configurations to access a new API are placed in the instances.js
file
api
├─ index.js # where api calls are made
├─ instances.js # mapper to create the body to send to the API
└─ interceptors.js
# ACL
WARNING
More information coming soon.
# I18n

Vue I18n (opens new window) is the plugin used to help us handle Internationalization.
After cloning the repository, the folder src/locales
will be empty. Translations are not versioned.
During the docker-compose up
process, webtranslateit will be instantiated and translations files will be pulled from there.
For each language supported exists, 1 generic file, 1 error file and 1 file for each module:
en.json # always loaded
errors.json # always loaded
users.en.json # only required for users module
roles.en.json # only required for roles module
<module>.en.json # other modules
# Generic
<lang>.json
file must be contain only generic or global functionalities translations of the application.
# Errors
Error translations are usually duplicated. The API must contain all the validations of an application however, to improve the usability, some of those validations end up being duplicated on the frontend applications. It creates a sync problem, every time we want to change a translation, we have to do it in two or more places.
To avoid this scenario, errors.<lang>.json
must be generic enough to be used by both frontend and API. This way changing a translation will be reflected both.
{
"100": "{field} is mandatory",
"101": "{field} must have between {min} and {max} characteres",
"102": "{field} must have at least {min} characteres",
"103": "{field} must have maximum {max} characteres",
"104": "{field} is invalid",
...
}
# Modules
Each module has its own translations file. It bring us some advantages like:
- Lazy load translations as modules are loaded. If the user don't have access to a specific module, the respective translations will never be loaded;
- There is no need to keep tracking where keys are being used. It is self explanatory;
- Developers can be confident that changing a key in the Users file won't have impact in the Roles module.