-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture Overview
This page outlines significant architectural decisions at a high level. This does not aim to go into detail about the individual client and server projects but rather the significant top-level design choices common to both.
- Author: Alex Wilkes <adw4236@rit.edu>
As a standard web application, the client and server have been split up into two architectural tiers that each have several layers within them. Each of these tiers are discussed more within their respective pages under the Code Architecture and Design section of the wiki.

Below are some of the advantages of seperating the client and the server into two seperate projects.
- The client can be configured to run independently in a development environment allowing us to make use of tools like the ones that come with create-react-app to make development smoother.
- A custom build cycle with webpack can be used to make the client application as efficient as possible (again, handled by create-react-app unless it is ejected).
- Cypress tests do not need to initialize the entire API server (end-to-end tests do).
- Integration / API tests can be created without needing to worry about the client side.
For more resources on why this type of architecture suites modern Node.JS applicaitons, see the webpack documentation.
The client is a single page web application and routing the user to different pages is handled by react-router. This means that all requests to the server must serve up the single page adn react-router will handle determining which one to display.
This is conflicting with the idea of serving an API from the same server however
because we cannot both fulfil the API request and serve the single page. To solve this
issue, express allows the use of routers which
gives us the ability to emulate an entire express server either on a subdomain, or from
a path prefix like fitrskills.com/api/.... All requests to this group of routes will
be handled by the API server abstraction while all others will be handled by the client
server abstraction.
It is best to learn by example, so if this is confusing, reference the three files
app.js, api.js, and client.js in /server/src to see this interaction play out.
If at any point the app can no longer be single paged, all additional routes can be
defined in client.js without fear of breaking the API.
In general, the API is meant to emulate RESTful APIs as much as possible, but there are
some exceptions to this where stateful behavior is the best solution. This API is meant
to be private, if a public API is ever created it is highly recommended to create another
express abstraction similar to api.js and client.js to handle such an API.
The API uses cookies and session state to store user's information so that we can customize the data that is sent back to the client.
We currently also use cookies for authentication since that is the default for passport. This may be suited fine, but a public API will likely require an implementation of OAuth2.0 which passport also supports.
As a RESTful API, it is important to define the uses for the various HTTP methods. All of these requests have access to the session state although it should only be used for storing a reference to user specific data such as their profile.
| Method | Description |
|---|---|
| GET | No database updates are caused. |
| POST | New database entries are created. |
| PUT | Currently not used. |
| PATCH | Database entries are updated. |
| DELETE | Database entries are removed. |
Some further considerations
- One request should not perform multiple duties (i.e. create and update).
- POST, PATCH, and DELETE should simply return a status and in most cases not data.
- Multiple methods can and often should be present on the same path (i.e. POST & PATCH
/profile) - Query parameters are typically preferred over path parameters
Note: Our team did not have this well-defined at the start of the project so if there are discrepancies, they should be fixed.
This application has not had much time to develop and therefor the architecturally significant requirements are sparse.
In order to facilitate all three roles, the mongoose schema maintained one list of user credentials but split their profiles into different types. This allows us to store different information on a user's profile based on the types of interaction they will have with the website.
The core of this website revolves around skills, so it is important to have a robust architecture around these skills. Each skill is represented as an object in the database and is treated as such in the frontend as well. This allows us to keep a list of skills along with more detailed information about them to ensure there is no confusion.
Both educators and employers are assigned to organizations. An organization itself is independent from any one account, but multiple accounts can be associated with the same organization. This allows for flexibility when it comes to who should have permissions to add, edit, or remove courses and job postings.
Members of the previous team have not been experts in security and there likely will need to be a security audit before the application goes live.
We have however kept the following considerations in the back of our mind and done our best to mitigate attacks.
- Password Hashing: We use passport-local-mongoose for this.
- No hardcoded credentials: We make use of environment variables
- Path Traversal
- Mongoose prevents SQL injection
It is the controller layer on the server that is responsible for ensuring sensitive data is not leaked from the server as well as ensuring nefarious data scrubbed. Our general philosophy is to use as many third party libraries that are trusted in any situations where security may be a concern.
Some security considerations we have thought about but have yet to actively mitigate
- CSRF
- XSS
- Requirements Gathering
- Templates
- Functional Requirements
- Non-Functional Requirements