Framework to improve reusability of code once written for different project. Framework force you to write code the way that can be resuable for different applications.
For example. If You want to have different application with same layout and same user management but with different content. So You can reuse "users" and "admin" module in both projects.
Same with authorization or mail management or static page generation.
In my 10-year career, I've written a lot of code, much of which is now obsolete. Some of my projects were great, but I often end up abandoning them due to burnout. Then, a new idea strikes, and I find myself writing user registration, email management, etc., all over again, only to burn out once more. Eventually, I consider reviving an old project but realize that the technology I used is outdated and my approaches were flawed, rendering the code unusable. Sometimes, I even discover it was written in a different language for reasons I can't recall.
For example, I have a user management system from my last project that I need to update and integrate. This process wasn't straightforward and resulted in two user management systems that both require maintenance, doubling my burnout. The same problem arises with email management, UX, UI, and other components.
I thought, wouldn’t it be nice to have some larger building blocks so that even if I switch projects, I’ll be ready?
That's why I created a framework that supports modularization and reusability, allowing me to add modules seamlessly. For example, by simply adding a module name, I can streamline my development process and reduce redundancy.
Example:
{
"app1": {
"host": ["app1.localhost"],
"modules": ["custom-module", "users", "page", "my-layout", "mail"]
},
"app2": {
"host": ["app2.localhost"],
"modules": ["users", "page", "security", "cors", "mail", "my-layout", "cron"]
}
}
Different modules can add different functionality. For example "mail" is my private (for now) module that adds mailSender service that use AWS mailer to send emails and read all templates from app/{appName}/mail/{names}.html. I wrote this once and now can reuse it in two different project app1 and app2. I just need to update the templates in app1 and and app2 or leave it and have default template.
Perfect example is "sitemap" as well, by adding it to project would be super easy.
This is just example, but You can have cms-module or admin-module and adjust its behavior for any project.
https://github.com/Mupli/mupli-examples
Framework is a structure for
- monolith modularity ?
And mainly created for monolith servers.
It can be be used for microservices, but you will need to split code base or deploy same codebase but with tags ( tags="user-microservice"). I think for Startups and MVP go with monolith approach. If you start earning and find bootlenecks then split project in multiservices.
Important Tip: Code sharing between projects should be done via modules. If you share using node js imports/require you will endup with spagethi code and yout code will not be resuable nor split_able.
node app/app.js
or with tags:
node app/app.js tags=dev,sit,some-other
Note this will run only projects with tags.
- process inheritance modules
- init (app, bootConfig)
- services -> services(appName, ctx)
- moduleExtentions -> moduleExtentions(appName)
- routes({appName}, serviceCtx) -> fn(app)
- ws -> fn(app)
- dispatch //TODO
- context -> context(appName, ctx)
- middlewares (appName, config) => return fn (ctx) {} // global middlewares
- action (route action)
- onError (appName, e, ctx)
- wsContext -> context(appName, ctx)
- wsmiddlewares -> (ctx) // global middlewares
- action (ws action)
- onError (appName, e, ctx)
One module can add extensions for others
moduleExtensions(appName, ctx) {
const me = this;
return {
securityExt: async function (obj) {
// handle data from other modules
},
otherNameExt : async function (obj) {
// handle ..
},
};
}
export const myModule = {
moduleName: "myModuleName",
routes: (appName, serviceCtx) => {
return {
"/page": (ctx) => {
return "OK";
},
};
},
};
config/apps.json
{
"myapp": {
"host": ["localhost"],
"modules": ["myModuleName"]
},
"myapp-still-in-development": {
"host": ["dev.localhost"],
"arch": "modular",
"tags": ["dev", "someother"],
"modules": ["users", "page"]
}
}
app/app.js
new Mupli() //
.module(myModule)
.listen(3000);
node app/app.js
- page - html page renderer module
- api - Rest module
- mail - mail sender module
- newsletter
- user
- register
- product
- cron
- aws
- files
// file usersModule.js
export const usersModule = {
moduleName: "usersModule",
services(appName, ctx) {
return {
userServices: new UserServices(ctx.dbConnection);
}
}
}
//file : myModule.js
export const myModule = {
moduleName: "myModuleName",
init(appName){
// do stugff
}
services(appName, ctx) {
return {
//userServices can be because was before in config.json (modules: ["usersModule", "myModuleName"])
myService: new MyService(dbConnection, ctx.userServices),
myEmailService: //...
}
}
middlewares: (appName) =>([
globalMiddleWare,
(ctx) => {
console.log("all request log")
},
isMethod("post", "patch"), //use mupli-middlewares
//or manual middleware
(ctx) => {
if (ctx.req.is("POST")) {
// no post is allowed
// return values will break stop the invocation
return ctx.res.status(403);
}
}
])
routes: (appName, serviceCtx) => { //no ctx details - request, response, ...
return {
"/page": [
localMiddleware,
(ctx)=>{
console.log("Invoked middle wears")
},
isMethod("get"),
isOwner("dataCollection", "_id")
hasRoles("PRODUCT_CREATOR", "ADMIN"),
validateGroupAccess(),
isAuthenticated()
async (ctx) => {
// my do logic
const myData = await ctx.req.json()
const savedMyDate = await myService.save(myData)
await ctx.userService.saveRelation(ctx.auth.id, myData.id)
return ctx.res.ok();
}
]
};
},
};
Mupli allows to inherit functionality of other modules:
For example my new module can be full fledge application with UI that use "page" and "api" modules .
import path from "path";
import { fileURLToPath } from "url";
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
class MyAppModule {
moduleName="myapp"
appPath= currentFileDirectory +"/src";
modules = ["page", "api"] //limited only to this module
// arch = "domain" // optional structure
}
path of library can be custom. But In most caseses it will be ".node_modules/myapp/src"
module structure:
- /src
- /page
- index.html
- something.html
- /api
- ...
- /page
in ourc config apps.json
{
"myCurrentApp": {
"hosts":["localhost"],
"modules": ["myapp", "other"],
}
}
Go to /something or /index