✨ This workspace has been generated by Nx, a Smart, fast and extensible build system. ✨
npm install --global nx@latest
Git Bash
to run the commandsVSCode
as the code editoryarn
to install the dependencies.env
file in the apps/chat
directoryTo start the development server run yarn dev:chat
. Open your browser and navigate to http://localhost:4200/. Happy coding!
yarn lint
to lint the codebaseyarn lint:fix
to fix the linting issuesyarn format
to format the codebaseyarn format:fix
to fix the formatting issuesWe are using monorepo architecture to manage the codebase. The workspace is divided into multiple applications and libraries. Each application is a standalone application and each library is a reusable codebase.
Workspace will be managed by Nx
which is a smart, fast and extensible build system.
All applications are located in the apps
directory. Each application is a standalone React application and has its own codebase.
chat
: Chat applicationadmin
: Admin applicationCurrently, we only focus on the chat
application.
All libraries are located in the libs
directory. Each library is a reusable codebase and can be used by multiple applications.
ui
: UI elements library, the components are stateless
and dumb
components
: Shared components library, the components are stateful
and smart
perform some logic through context
and hooks
core
: Core library, contains the core logic of the application, could be reused by multiple applications e.g. web, mobile, desktoptransports
: Transport layer library, contains the logic to communicate with the server through mezon-js
librarystore
: State management library, contains the logic to manage the state of the application using redux
and redux-toolkit
assets
: Assets library, contains the assets used by the applications and librarieslogger
: Logger library, contains the logic to log the messagesutils
: Utility functions libraryCodebase is divided into multiple dependencies which are managed by Nx
. There are 2 types of modules:
the libraries are shared by multiple applications and could be reused by multiple applications.
There are several types of libraries:
ui
: UI elements library, the components are stateless
and dumb
components
: Shared components library, the components are stateful
and smart
perform some logic through context
and hooks
logic
: Logic library, contains the core logic of the application, could be reused by multiple applications e.g. web, mobile, desktoputils
: Utility functions libraryThe dependencies are depend on each other based on the following rules (<- = depend on)
:
apps
<- libs
✅libs
<- libs
✅components
<- ui
✅components
<- store
✅store
<- transports
✅store
<- utils
✅Bad dependencies:
apps
<- apps
❌libs
<- apps
❌components
<- apps
❌ui
<- components
❌store
<- components
❌transports
<- store
❌utils
<- store
❌utils
<- transports
❌mobile libs
<- web libs
❌web libs
<- desktop libs
❌The dependency graph is managed by Nx
and could be visualized by running the following command:
npx nx graph
the output will be the dependency graph of the workspace.
how to read the dependency graph:
apps
are the standalone applicationslibs
are the reusable codebaseapps
<- apps
, libs
<- apps
, ui
<- components
, etc.for example, we have bad dependencies between components
and apps
which are not allowed.
We are using one-way
data flow architecture to manage the data flow of the application. The data flow is unidirectional follow the Redux
pattern.
See more about the Redux
pattern here.
The core concepts are one-way
data flow and single source of truth
.
The application data flow is managed by some packages:
mezon-js
: The core package to communicate with the server through WebSocket
and REST
API
WebSocket
: send and listen to the messages from the serverREST
: send and receive the messages from the serverstore
: The state management package to manage the state of the application. store is divided into multiple slices, each slice is a standalone slice and has its own reducer, action, and selector.
slice
: A standalone slice of the store, contains the reducer, action, and selectorreducer
: A function to manage the state of the applicationaction
: A function to dispatch the action to the reducerselector
: A function to select the state from the storerouting
: The routing package to manage the routing of the application. The routing is managed by react-router-dom
package.
loader
to load the initial dataloader
will trigger the action
to fetch the data from the serverasyncThunk
to fetch the data from the server using mezon-js
packageasyncThunk
returns the data from the server and updates the state of the store by an extraReducers
functionuseSelector
how to layout the components and pages
We have sevaral layout components to handle layout based on the route:
/
- AppLayout
: The layout for the application/chat
-[logged in]- MainLayout
: The layout for the main pageMain
: The main page to render the global components/chat/server/:id
- ClanLayout
: The layout for the server page/chat/server/:id/channel/:id
- ChannelLayout
: The layout for the channel pagereact-router
v6 to manage the routing of the application, see more about the react-router
v6 hereAccess control is managed by the policies
slice. each user has it own permissions to access the resources. The permission is managed by the policies
slice.
There are several ways to manage the access control:
policies
slice and selectAllPermissionsUser
to get the permissions of the useruseSelector(selectAllPermissionsUser)
to get the permissions of the userUserRestrictionZone
to control displaying the components based on the user permissionsuseUserRestriction
to get the user restrictions based on the user permissionsToast notification is managed by the toasts
slice. each toast has it own message and type. The toast is managed by the toasts
slice.
Actions
addToast
: add a toast to the listremoveToast
: remove a toast from the listToast are displayed in the <ToastContainer />
component.
There are several ways to manage the toast notification:
addToast
action to add the toast to the listwithToast
meta // add toast notification to any action
return thunkAPI.fulfillWithValue(
value,
withToast({
message: 'Clan changed',
type: 'success',
})
);
// dispatch the addToast action directly
thunkAPI.dispatch(
addToast({
message: 'Clan changed',
type: 'success',
});
);
Error handling is managed by the errors
slice. each error has it own message and code. The error is managed by the errors
slice.
By default, the error is displayed as toast notification. in case you want to disable the toast notification, you could set the toast
meta to false
.
// No toast notification
return thunkAPI.rejectWithValue(
error,
withError({
toast: false
})
);
// toast with custom message
return thunkAPI.rejectWithValue(error, withError('Custom error message'));
// fully custom error
return thunkAPI.rejectWithValue(
error,
withError({
toast: {
message: 'Custom error message',
type: 'error',
theme: 'dark'
}
})
);
For the desktop
application, we are using electron
to build the application. The application's dependencies are managed by the apps/desktop/package.json
file. When building the application, the dependencies are installed in the apps/desktop/node_modules
directory.
The application performance is mostly affected by these factors:
memo
and useMemo
to prevent unnecessary re-renderuseEffect
and clear function
to prevent memory leakuseCallback
to prevent function changes referencestore
and memoizee
to cache the api callscustom hook
to manage the data and logic of the component, make sure that the custom hook group data of same level of abstraction, and not re-render the component when the unrelated data changesWe use several tools to measure the performance of the application:
React DevTools
: to measure the performance of the applicationChrome DevTools
: to measure the performance of the applicationUsing Prettier
and ESLint
to format the codebase. The codebase should be formatted before committing the code.
PascalCase
for the components and pagescamelCase
for the functions and variablesSee more about the naming convention here
See: https://github.com/electron/electron/issues/3331
./dist/executables/win-unpacked/mezon.exe --remote-debugging-port=8315
chrome://inspect
Configure...
and add localhost:8315
to the listinspect
to open the DevTools