Skip to content

Commit

Permalink
refactor(app): create loosely coupled engine
Browse files Browse the repository at this point in the history
  • Loading branch information
acellam committed Aug 11, 2024
1 parent b91bf03 commit eb7d1b9
Show file tree
Hide file tree
Showing 42 changed files with 203 additions and 340 deletions.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#PRODUCTION
RDBMS_DATABASE_URI="mysql://DATBASE_USER:DATABASE_PASSWORD@DATABASE_HOST:DATABASE_PORT/DATABASE_DB"
NOSQL_DATABASE_URI="mongodb://DATABASE_HOST:DATABASE_PORT/DATABASE_DB"
NOSQL_DATABASE_ADAPTER="mongodb"
API_BASE='/v1/'
9 changes: 0 additions & 9 deletions @types/express/index.d.ts

This file was deleted.

32 changes: 22 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,29 @@ A component based framework for NodeJs Applications
## Architecture
<img src="./architecture.png" width="500" >

We are creating a structure for developing large scalable, maitanable Nodejs applications. We mash up ideas from component based architecture, MVC, Entity Framework and Repository Pattern. We want different teams to focus on creating their domain apps in the components with ease. Fractal Js comes with authentication out of the box based on JWT
We are creating a structure for developing large scalable, maitanable Nodejs applications. We mash up ideas from component based architecture, MVC, Entity Framework and Repository Pattern. We want different teams to focus on creating their domain apps in the components with ease.

## Issues, suggestions and feature requests
We are actively maintaining this boilerplate, please report any issues or suggestion for improvement at https://github.com/fractalerp/fractal-js/issues
We are actively maintaining this framework, please report any issues or suggestion for improvement at https://github.com/fractalerp/fractal-js/issues

## How to run the project
Prerequisite: Install git, node package manager, webpack CLI, grunt CLI. This framework also uses fractalerp [Active Record Js](https://github.com/fractalerp/active-record-js) for defining models.

1. Create the following environment variables in your node project.
```env
RDBMS_DATABASE_URI="mysql://DATBASE_USER:DATABASE_PASSWORD@DATABASE_HOST:DATABASE_PORT/DATABASE_DB"
NOSQL_DATABASE_URI="mongodb://DATABASE_HOST:DATABASE_PORT/DATABASE_DB"
NOSQL_DATABASE_ADAPTER="mongodb"
```

## Development and contribution
Prerequisite: Install git, node package manager, webpack CLI, grunt CLI
2. Then you can create your `nodejs` apps in the `components` folder. The projects in that folder are autoloaded at run time. See the sample `Task` project in the same folder. A proper documentation will be provided in the future. Also tools will be provided to create this structure. Refer to the github project management dashboard to see what is coming up.

## 🫶 Projects using this package
See the projects using this package in action.
- [Fractalerp core](https://github.com/fractalerp/fractal-core)

## Issues, suggestions and feature requests
We are actively maintaining this boilerplate, please report any issues or suggestion for improvement at https://github.com/fractalerp/fractal-js/issues

To contribute, fork and clone.

Expand All @@ -50,10 +66,6 @@ Run the unit test continuously during development:

> npm run test:dev

Run the end to end test during development:

> npm run test:e2e:dev

## Scripts
While developing, you will probably rely mostly on `npm start`; however, there are additional scripts at your disposal:

Expand All @@ -67,8 +79,8 @@ While developing, you will probably rely mostly on `npm start`; however, there a
|`test`|Runs lint, build, unit tests with mocha and generates a coverage report|
|`test:dev`|Runs mocha and watches for changes to re-run tests; does not generate coverage reports.|
|`test:unit`|Runs unit tests with mocha and generates a coverage report.|
|`build:prod`|Build app optimized for production|
|`build:dev`|Build app optimized for debugging.|
|`build:release`|Build app optimized for production|
|`build:development`|Build app optimized for debugging.|
|`lint`|Lint all `.js` files.|
|`lint:fix`|Lint and fix all `.ts` files.|

8 changes: 6 additions & 2 deletions index.ts → app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import fractaLog, { fractalLogger } from "./config/logger";
import { csrfHandler } from "./middleware/csrf.middleware";
import { Environments } from "./utils/constants";
import { getJWT } from "./utils/helpers";
import { FractalRouter } from "./routes/fractal_router";

export class FractalApp {
export class FractalJs {
public express!: express.Application;
public session!: express.RequestHandler;
public server!: http.Server;
Expand All @@ -45,6 +46,8 @@ export class FractalApp {
this.express.use(csrfHandler);
//
this.express.use(this.initPassport());
// Main app router
new FractalRouter(this);

this.loadComponents();

Expand All @@ -69,6 +72,7 @@ export class FractalApp {
this.express.enable("trust proxy");
// set up logging
this.express.use(morgan("combined", { stream: fractaLog.stream } as any));

this.server = http.createServer(this.express);

useragent(true);
Expand Down Expand Up @@ -210,4 +214,4 @@ export class FractalApp {
};
}

export default new FractalApp();
export default new FractalJs();
3 changes: 0 additions & 3 deletions app/controllers/fractal_controller.ts

This file was deleted.

6 changes: 0 additions & 6 deletions app/models/fractal_active_model.ts

This file was deleted.

Empty file removed app/views/index.ts
Empty file.
11 changes: 11 additions & 0 deletions components/task/adapters/task_adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { TaskEntity } from "../public/entities/task_entity";

export class TaskAdapter {
taskEntity: TaskEntity;

constructor(taskEntity: TaskEntity) {
this.taskEntity = taskEntity;
}

toJson = () => JSON.stringify(this.taskEntity);
}
Empty file.
7 changes: 0 additions & 7 deletions components/task/app/controllers/index.ts

This file was deleted.

Empty file removed components/task/app/jobs/index.ts
Empty file.
Empty file removed components/task/app/logic/index.ts
Empty file.
22 changes: 0 additions & 22 deletions components/task/app/models/project.ts

This file was deleted.

22 changes: 0 additions & 22 deletions components/task/app/models/task.ts

This file was deleted.

Empty file.
Empty file.
Empty file removed components/task/app/views/index.ts
Empty file.
Empty file removed components/task/config/index.ts
Empty file.
27 changes: 0 additions & 27 deletions components/task/config/router.ts

This file was deleted.

59 changes: 59 additions & 0 deletions components/task/controllers/tasks_controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Request, Response } from "express";

import { StatusCodes } from "http-status-codes";
import { TaskRepository } from "../public/repositories/task_repository";
import { TaskAdapter } from "../adapters/task_adapter";
import { TaskEntity } from "../public/entities/task_entity";

export class TasksController {
taskRepository = new TaskRepository();

public addNewTask = async (req: Request, res: Response) => {
const taskEntity = await this.taskRepository.create({
name: req.body.name,
description: req.body.description
});

const json = new TaskAdapter(taskEntity).toJson();

return res.status(StatusCodes.CREATED).json(json);
};

public getTasks = async (_req: Request, res: Response) => {
const taskEntities = await this.taskRepository.read({});
const taskListJson = taskEntities.map((taskEntity: TaskEntity) => new TaskAdapter(taskEntity).toJson());

return res.status(StatusCodes.OK).json(taskListJson);
};

public getTaskWithID = async (req: Request, res: Response) => {
const taskEntities = await this.taskRepository.read({ id: req.params.id });
let json = {};

if (taskEntities.length > 0) {
json = new TaskAdapter(taskEntities[0]).toJson();
}

return res.status(StatusCodes.OK).json(json);
};

public updateTask = async (req: Request, res: Response) => {
const taskEntity = await this.taskRepository.update({
id: req.body.id,
name: req.body.name,
description: req.body.description
});

const json = new TaskAdapter(taskEntity).toJson();

return res.status(StatusCodes.OK).json(json);
};

public deleteTask = async (req: Request, res: Response) => {
await this.taskRepository.delete({
id: req.body.id
});

return res.status(StatusCodes.OK).json({});
};
}
Empty file removed components/task/db/index.ts
Empty file.
12 changes: 6 additions & 6 deletions components/task/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { fractalLogger } from "../../config/logger";
import { FractalApp } from "../../index";
import { TaskRouter } from "./config/router";
import { FractalJs } from "../../app";
import { TaskRouter } from "./routes/router";

export class TaskComponent {
public taskRouter!: TaskRouter;
public fractalApp!: FractalApp;
public fractalJs!: FractalJs;

constructor(fractalApp: FractalApp) {
constructor(fractalJs: FractalJs) {
try {
this.fractalApp = fractalApp;
this.taskRouter = new TaskRouter(fractalApp);
this.fractalJs = fractalJs;
this.taskRouter = new TaskRouter(fractalJs);
} catch (error) {
fractalLogger.error(`Task Component: Failed to create component :( ", ${error}`);
}
Expand Down
File renamed without changes.
File renamed without changes.
20 changes: 20 additions & 0 deletions components/task/models/task_model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ActiveRecord } from "@fractalerp/active-record-js"

export interface ITaskModelDocument {
name: string;
description: string;
}

const TaskModelSchema = {
name: {
type: String,
required: true,
unique: true
},
description: {
type: String,
default: null
}
};

export const TaskModel = new ActiveRecord<ITaskModelDocument>("Task", TaskModelSchema);
File renamed without changes.
File renamed without changes.
28 changes: 28 additions & 0 deletions components/task/routes/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { NextFunction } from "express-serve-static-core";

import { TasksHomeRoute } from "../routes/tasks_home_route";
import { TasksRoute } from "../routes/tasks_route";
import { FractalJs } from "../../../app";

export class TaskRouter {
public fractalJs!: FractalJs;

constructor(fractalJs: FractalJs) {
this.fractalJs = fractalJs;
// white list public routes
this.allowPublicRoutes();
// Add routes
new TasksHomeRoute(fractalJs);
new TasksRoute(fractalJs);
}

private allowPublicRoutes() {
this.fractalJs.express.all(
`${process.env.API_BASE}tasks/*`, async (_req: any, _res: any, next: NextFunction) => {
// Add public routes not to authenticate
// await this.authenticateApi(this.fractalJs, req, res, next);

next();
});
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Request, Response } from "express";
import { FractalApp } from "../../../../index";
import { FractalJs } from "../../../app";

export class TaskHome {
public routes = (app: FractalApp) => {
const authEndpoint = `${process.env.API_BASE}tasks`;
export class TasksHomeRoute {
public constructor(fractalJs: FractalJs) {
const authEndpoint = `${process.env.API_BASE}tasks/home`;
// GET endpoint
app.express.route(authEndpoint)
fractalJs.express.route(authEndpoint)
// GET endpoint
.get((_req: Request, res: Response) => {
// Get all contacts
Expand All @@ -16,5 +16,3 @@ export class TaskHome {
});
};
}

export default new TaskHome();
21 changes: 21 additions & 0 deletions components/task/routes/tasks_route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { TasksController } from "../controllers/tasks_controller";
import { FractalJs } from "../../../app";

export class TasksRoute {
public tasksController!: TasksController;

public constructor(fractalJs: FractalJs) {
this.tasksController = new TasksController();
const tasksEndpoint = `${process.env.API_BASE}tasks`;
/* Get, Add tasks */
fractalJs.express.route(`${tasksEndpoint}`)
.get(this.tasksController.getTasks)
.post(this.tasksController.addNewTask);

/* Task details, update and delete */
fractalJs.express.route(`${tasksEndpoint}/:id`)
.get(this.tasksController.getTaskWithID)
.put(this.tasksController.updateTask)
.delete(this.tasksController.deleteTask);
}
}
File renamed without changes.
Loading

0 comments on commit eb7d1b9

Please sign in to comment.