This file contains some comments about the decisions which were made.
-
The goals are:
- Minimal source code repetition which includes a minimal amount of boilerplate code.
- Short names.
- Short files.
- Expectable behavior.
- Minimal costs of changes, especially of infrastructure changes.
- Easily testable via command line tools and automated tests.
-
The general source code file and folder structure is
<src-folder>/<app>/<domain>/<layer>/<file>.<src-folder>:config: For config files.sql: Contains sql scripts for development and database setup.src: PureScript and JavaScript development source files.test: PureScript and Json source files for testing. Test can depend onsrcbut not vice versa.
<app>:Client: Code which is only used in the client application. (Not implemented)Server: Code which is only used in the server application.Servercan only depend onShared. The entry point for the server application is calledMain.pursShared: Code which is of general usage.
<domain>:Shell: For the entry point of the application and to direct to other domains.Shellcan depend on other domains. Other domains cannot depend onShellUseretc.: Domain code. The domain should be independent. Function names should be equal across domains with different signature depending on the context.Shared: Code which can be used by all domains. Other domains can depend onShared.Sharedcannot depend on other domains.
<layer>:Api: Entry points of requests and validation logic. This layer can depend onApplication.Application: Business application layer. Effects can only be used with interfaces. If the business logic is just one call to another layer, the call is made directly and the declaration inApplicationis omitted.Interface: Interfaces for persistence or application functions.Persistence: Persistence/storage/database layer.Util: Holds util functions. They can be accessed via interfaces.Type: Holds types which can be used by all layers of the domain. If a type is only used by one layer, it is placed directly under this layer.
<file>:- If there can be multiple implementations of the same layer, a distinct name is used.
Main.pursrefers to the general implementation/entrance point of a folder.- The ideal file length is two pages. Simple types are usually shorter, that's why they are placed under
Misc.purs. - The handle pattern was used for dependency injection (Van der Jeugt, 2018).
- The structure for holding the dependencies is called
Handle. - The function for getting an implementations is called
mkHandle. - The namespace for aggregating two or more handles is called
Aggregate.
- The structure for holding the dependencies is called
- The source code is written for qualified import, which is also mentioned at (Van der Jeugt, 2018).
- Actual Qualifiers like
import ... as Payloadshould be used sparingly because they can't be created automatically by the PureScript VS Code IDE. Also the names are arbitrary.
- Actual Qualifiers like
- The expression order in a code line should be from right to left.
- If the order of parameters, arrays, records, etc. is arbitrary, it should be ordered alphabetically in the source code.
- PostgreSQL:
- In order to create type-safe sql queries purescript-selda was used.
- As of the time of writing, there was a bug in the update functionality with
GENERATED ALWAYS AS IDENTITYthereforeSERIALwas used. (See issue). - As of the time of writing, there was a bug in PureScript with type synonyms therefore code was repeated. (See issue, search for
4105in the code).
- PureScript Payload:
- The
Failuretype did not fit for CORS, because it requires a custom header with every request. - Payload does not output validation errors. In order to see them, Payload was patched. (Compare https://github.com/hoodunit/purescript-payload/compare/master...jim108dev:master) (See issue)
- The
- Validation:
- purescript-simple-json is used for JSON encoding/decoding. Because the error field is dynamic the error structure is rendered with simple string concatenation (see src/Server/Shared/Api/Main.purs). This could be improved.
- Strings are represented by:
LongString: Between 1 and 1000 characters. The database type is unrestricted (TEXT).LowercaseString: Forces strings to be lowercase, e.g. for tags. The database type is unrestricted and case-insensitive (CITEXT).ShortString: Between 1 and 50 characters. The database type is unrestricted (TEXT).
- In order to safe on boilerplate code, all simple types like
UserIdare coded withtypeinstead ofnewtype.
- Security:
- purescript-node-jwt is used for token encoding/decoding. Only userId is encoded used. The expiration time is set to 1 hour.
- Tests:
- Setting
originin the tests was not possible (Error message:Refused to set unsafe header "origin"). I had to mock the function. - The test case request and response bodies can be found under
test/Server/<domain>/<file>. This way, they can be used via HTTPie and with automated testing.
- Setting
- Frontend:
- purescript-halogen-realword did not work for update user and post comments. (See issue)