A simple mechanism to transform a monolithic Vue application into an application based on Micro Services architecture
OSL-3.0 License
A simple mechanism to transform a monolithic Vue application into an application based on Micro Services architecture.
Combination of Vue
and NuxtJS
frameworks gives us the opportunity to build any web application we want.
Unfortunately, application built on this approach is monolithic and we cannot extend its behavior. Of course we can extend project with some elements, but these are small fragments that do not add much. In addition, NuxtJS
forces developers to have a specific directory structure (page, middleware, store, etc.). This gives us a rigid application built on specific principles.
VueMS
gives the possibility to divide the application into micro parts that use all Vue
+ NuxtJS
mechanisms, but do not have their limitations. Structure of these parts is identical to the monolithic application, however each module can operate separately, communicate and interact with one another. Modules can be both small elements (single component, plugin) and complex structures (components, plugins, middleware, store, pages).
Advantages of VueMS:
@ergonode/vuems
dependency to your project.npm install @ergonode/vuems
# or
yarn add @ergonode/vuems
@ergonode/vuems
to the buildModules
section in the nuxt.config.js
file. export default {
...
buildModules: [
['@ergonode/vuems', { /* module options */ }]
],
...
}
export default {
...
buildModules: [
'@ergonode/vuems'
],
vuems: {
/* module options */
},
...
}
modules
Object
{ npm:[], local: [] }
Object with all loaded modules.
required
Array
[]
Array with required module names.
modulesDir
String
modules
Local modules directory name.
vendorDir
String
vendor
Npm modules directory name. Directory is temporary and used by symbolic link.
nodeModulesDir
String
node_modules
Directory where installed npm modules are to be found.
vuex
Boolean
true
If Vuex library is used.
i18n
Boolean
false
If i18n plugin is used.
isDev
Boolean
false
Is development mode on.
logLoadedModules
Boolean
false
Log all loaded modules.
verbose
Boolean
true
Log module process.
directories
Object
{
assets: 'assets',
components: 'components',
config: 'config',
layouts: 'layouts',
middleware: 'middleware',
pages: 'pages',
plugins: 'plugins',
store: 'store',
}
Directory structure for module.
vuems: {
required: [
'@my/core',
],
modules: {
local: [
'@my/core',
'@my/authentication',
],
npm: [
'@test/users',
'@test2/import'
]
},
isDev: process.env.NODE_ENV !== 'production',
}
Modules are based on mechanisms Nuxt Modules, but they have no restrictions on the structure. The module can have any structure, which means it can be a single functionality or a large and complex business logic. We divide modules at our discretion and it is also our decision what structure they will have.
Modules can be divided into two types. The type determines the place from which the module is loaded.
modules
directory. These modules are only available in the project and are fully modifiable.modulesDir
option)npm i module-name
). These modules are unmodifiable and they are updated only by upgrading the npm package version.nodeModulesDir
option).vendorDir
option).The names of local modules are determined based on the directory structure. The adopted and recommended directory structure is based on the concept npm scope.
modules/
|-- @test/
|-- my-local-module/
|-- users/
The names of the npm modules are consistent with the approach of creating npm packages.
Example:
modules: {
local: [
'@test/my-local-module',
'users',
],
npm: [
'@npm/npm-module',
],
}
Each module needs several things to work properly:
index.js
entry file:In the project directory we create an entry file called index.js
, needed to run the module.
index.js
is created according to the Nuxt Modules rules,beforeModule()
- asynchronous function run before the module is loaded,afterModule()
- synchronous function started after the module is loaded,export async function beforeModule() {
// run before loding module
}
export default async function () {
// module logic
}
export async function afterModule() {
// run after loding module
}
config
directory:Module must have a config
directory with index.js
file. All available configurations can be placed in this directory.
index.js
: Main configuration file exporting object:
name
: Module name
String
true
⚠️aliases
: Module aliases needed for easy communication between modules
Object
true
⚠️relations
: Modules relations. Modules in this array are needed for proper module operation.
Array
false
replacements
: Component replacements. Replace any component from any module with your own.
Object
false
plugins
: Module plugins loaded globally.
Array
false
css
: Module css loaded globally.
Array
false
Example:
export default {
name: '@test/core',
aliases: {
'@Core': '/',
},
replacements: {
'@Core/components/coreComponent': '/components/myComponent',
},
relations: [
'@test/messages',
],
plugins: [
{ ssr: true, src: './plugins/axios' },
],
css: [
'./assets/scss/reset.scss',
]
};
routes.js
: Main routing configuration file for the module. Is closely related with Nuxt Router mechanism.
Example:
const Page = () => import('@Core/pages/settings/index').then(m => m.default || m);
const Tab = () => import('@Core/components/tab').then(m => m.default || m);
export default [
{
name: 'settings',
path: '/settings',
component: Page,
children: [
{
name: 'settings-main',
path: 'main',
component: Tab,
},
],
},
];
extends.js
: Mechanisms extending other modules.
extendComponents
: You can extend components from one module to components from another.
Object
false
Example:
// Definition
const Navigation: () => import('@Notifications/components/Navigation');
export default {
extendComponents: {
NAVIGATION_BAR: [
{
component: Navigation,
props: {},
},
],
},
};
The global this.$getExtendSlot()
function is used to download the component.
// Use in other module
<template v-for="(component, index) in extenededComponents">
<Component
:is="component.component"
:key="index"
v-bind="component.props" />
</template>
<script>
export default {
computed: {
extendedComponents() {
return this.$getExtendSlot('NAVIGATION_BAR');
},
},
};
</script>
extendRoutesChildren
: You can extend routing in any module.
Array
false
Example:
const TestSettingsTab = () => import('@Test/components/SettingsTab').then(m => m.default || m);
export default {
extendRoutesChildren: [
{
name: 'settings', // existing router name what we want extend
children: [ // array with router to extend
{
name: 'settings-test',
path: 'test',
component: TestSettingsTab,
},
],
}
];
}
dictionaries
: You can extend dictionaries.
Array
false
Example:
export default {
dictionaries: [
{
stateProp: 'currencies',
dataFormat: {},
requestPath: '/dictionary/currencies',
},
]
}
extendStore
: You can extend the existing Vuex store.
Object
false
Example:
const TestStore = () => import('@Test/extends/store/test');
export default {
extendStore: {
Test: TestStore,
},
}
If we have existing Test
store and we want extend it.
extendMethods
: You can insert any method from module
Object
false
Example:
// Definition
export default {
extendMethods: {
'@Test/store/test/action': () => {
console.log('test');
}
},
}
The global this.$getExtendMethod()
function is used for call extended methods.
// Use in other module
export default {
create({ state }) {
...
this.$getExtendMethod('@Test/store/test/action');
...
}
}