TUTORIAL
1. Starting a new project2. Local development3. Version control4. Project management5. Remote environments6. Environment variables and secrets7. Databases and files8. Cloud services and Terraform9. full-stack-template specific details10. Production setup11. Running in production12. Creating a custom command13. Creating a custom plugin14. Creating a custom template15. Creating a zone16. Zone maintenance17. Zone monitoring18. Zone recoveryAPPENDIX A: Technology tutorialsAPPENDIX B: Software designAPPENDIX C: Modern server infrastructureAPPENDIX D: SecurityAPPENDIX E: Data protection and privacy (GDPR)

APPENDIX B: Software design

Modular structure

Many tutorials introduce you a monolithic application structure: put all application state in this directory, put all application actions into this another directory. This is easy to understand and works ok for a small application, but it starts to become problematic once the codebase grows larger. Another approach would be to split the frontend into separate micro frontends and the backend into separate microservices. But this can be a bit overkill approach for most applications. When in doubt, a monolithic implementation with a modular structure is often the best approach for quickly building the first MVP or prototype. When done correctly, the modular structure can easily be split into separate micro frontends and microservices later if required.

Whether you are building a monolithic implementation or separate micro frontends and microservices, an application or API codebase should always be divided into loosely coupled highly cohesive parts by using a modular structure. Even if the monolithic implementation is not going to be split into micro frontends and microservices later, the modular structure provides many benefits, for example:

  • When making a change, it's easier to see how widely the change might affect the application.
  • When implementing a new feature, there is no need to jump around in the codebase as much.
  • It's easier for a new developer to implement new features without knowing the whole codebase.
  • Once the application grows and time passes, it's easier to rewrite some parts of the application using a newer technology without affecting the other parts.

The following guidelines usually work well when building a modular monolithic implementation:

  • Create directory structure mainly based on domain concepts (billing, management, trip, ...) instead of technical type or layer (actions, containers, components, css, utils, ...).
  • Inside these domain based modules you can use technical type or layer as folder structure, if you wish, but it is not always necessary.
  • Use event-based communication to resolve any circular dependencies between the modules. That is, one module produces events that the other modules can listen to, if they are interested in such an event.

Think about the following concepts while you are working with your project:

  • Loose coupling: Aim for loose coupling between different parts by defining a clear contract between the parts, minimizing the number of dependencies and avoiding circular dependencies altogether.
  • High cohesion: Aim for high cohesion by putting closely related logic in the same place.
  • Responsibility: Name the part according to its responsibility. The part should implement only such logic that it's responsible for and nothing else.

TODO: DRY, KISS, YAGNI, GRASP, SOLID TODO: https://jaxenter.com/promising-new-metric-track-maintainability-154195.html TODO: https://www.tutorialspoint.com/software_engineering/software_design_basics.htm

API design

GraphQL and REST API implementations should be stateless. Stateless means that a service does not keep any state in memory or on local disk between requests. That is, all state resides either on UI, on database, or on some other external system. Services should be stateless because multiple instances of the same service will be run in parallel, and the request that an UI or 3rd party system makes, may be forwarded to any them. In addition, you should be able to publish a new version of the service any time without causing interruptions, which is harder to do if service is stateful. A stateless service should not:

  • Cache data in local memory (tip: use Redis as cache, or keep state in UI and use JWT tokens if necessary)
  • Use local disk for permanent data (tip: use object storage buckets)
  • Define request rate limits (tip: define rate limits in Kubernetes ingress)
  • Use local timers to execute jobs (tip: use Kubernetes cron jobs)
  • TODO stateless websockets

GraphQL API design

TODO

RESTful API design

Some good articles on REST API design:

Relational database design

TODO: conventions for audit log and/or journal tables. Take GDPR into account.


Next: APPENDIX C: Modern server infrastructure

1. Starting a new project
2. Local development
3. Version control
4. Project management
5. Remote environments
6. Environment variables and secrets
7. Databases and files
8. Cloud services and Terraform
9. full-stack-template specific details
10. Production setup
11. Running in production
12. Creating a custom command
13. Creating a custom plugin
14. Creating a custom template
15. Creating a zone
16. Zone maintenance
17. Zone monitoring
18. Zone recovery
APPENDIX A: Technology tutorials
APPENDIX B: Software design
APPENDIX C: Modern server infrastructure
APPENDIX D: Security
APPENDIX E: Data protection and privacy (GDPR)
Home
Docs
Tutorial
Plugins
Templates
Extensions