diff --git a/README.md b/README.md index dcaa38d..b6c090e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # The MarsBased Handbook 🚀 -Welcome to __MarsBased__! This is the first thing you should read when boarding the __MarsBased__ spaceship. +Welcome to **MarsBased**! This is the first thing you should read when boarding the **MarsBased** spaceship. MarsBased is an all-remote development consultancy from Barcelona, building web and mobile products using Ruby on Rails, Python and JavaScript. We work at the intersection of business and technology, to help companies with their innovation projects using bleeding-edge technologies like AR/VR and AI. @@ -48,6 +48,7 @@ For now, we have the following resources available: 1. [Our Git & Commit guidelines](/guides/development/git-guidelines.md) 1. [Some of our used programming patterns](/guides/patterns/README.md) 1. [Angular guidelines](/guides/development/angular-guidelines.md) +1. [Angular 17 guidelines](/guides/development/angular-17-guidelines.md) 1. [React guidelines](/guides/development/react-guidelines.md) 2. [Back-end guidelines](/guides/development/back-end-development-guidelines.md) 3. [Code reviews guidelines](/guides/development/code-reviews-guidelines.md) @@ -65,4 +66,5 @@ For now, we have the following resources available: 1. MarsBased YouTube channel # Contributing + We encourage you to contribute! Please check out the [Contributing guides](./CONTRIBUTING.md) for guidelines about how to proceed. diff --git a/guides/development/angular-17-guidelines.md b/guides/development/angular-17-guidelines.md new file mode 100644 index 0000000..2d85ca4 --- /dev/null +++ b/guides/development/angular-17-guidelines.md @@ -0,0 +1,81 @@ +# MarsBased Angular 17 Style Guide + +### 🚧 This guide is focused on Angular 17, for older versions see [here](./angular-guidelines.md) 🚧 + +We follow the official and opinionated [Angular coding style guide](https://angular.dev/style-guide) as the primary source of best practices and conventions. + +## Standalone Components + +In the latest versions of Angular, it is recommended to use standalone components. These are components that do not have a direct relationship with other components and can be used independently. Previously, all elements were organized in modules, and using an element from a module meant loading the entire module. + +Now, with standalone components, each component is used individually, which improves application load time and performance. + +## Single Responsibility Services with State Management + +In Angular, services should adhere to the Single Responsibility Principle, meaning each service should focus on a specific domain or functionality. By doing so, services become easier to maintain, test, and reuse. + +Additionally, these services should manage their state using signals, allowing components to subscribe to state changes efficiently. Signals provide a reactive way to handle state, enabling components to subscribe to state changes and automatically update when the state changes. + +## Smart and Dumb (Presentational) components + +The only goal of a presentational component is to keep the UI logic isolated. It doesn't have access to injected business services. It communicates using @Input and @Output data flows.Presentational components communicate using Input and Output only. + +A smart component, or smart container, injects business services and keeps a more complex state. Its goal is to orchestrate the communication and behaviour between data services and presentational components. Smart components communicate to the rest of the app using injected services, Input an Outputs. + +Having a separation between smart and presentational components adds the advantages: + +- Easier to write and more focused tests. +- Isolated UI state. +- Better component reusability. + +## Structuring Application Architecture Using Routes + +In Angular, organizing the application's architecture by following the routes can enhance readability, maintainability, and scalability. Similar to other frameworks, using a route-based folder structure helps developers quickly locate components associated with specific routes, improving overall development efficiency. + +### Folder Structure + +Within the src directory, create a routes folder that mirrors the application's routes. Each route should have its own folder containing all related components, services, etc. This structure helps keep the project organized and makes it easier to manage and scale the application as it grows. + +Here's an example of how to structure the folders for an application with a users route: + +``` +src/ + app/ + routes/ + users/ + # users components +``` + +## State Interface + +Maintaining common interface components, such as modals, popups, or loading spinners, in a parent component common to the entire application is a recommended practice. This approach helps in managing the application's UI state efficiently and ensures a consistent look and feel across the application. + +By centralizing the management of common interface components in a parent component, you can: + +- Reduce Redundancy: Avoid duplicating code for common UI elements across multiple child components. +- Ensure Consistency: Maintain a uniform appearance and behavior for UI elements, ensuring a consistent user experience. +- Simplify Maintenance: Make it easier to update and manage UI components, as changes are made in a single place rather than across multiple components. + +## Input signals + +Components in Angular should receive input data through inputs, specifically as input signals. This practice becomes particularly advantageous when writing code in a reactive style using signals. + +### Advantages of Using Input Signals + +#### Reactive Style + +By using signals as inputs, all the component's input data become signals themselves. This allows for easily deriving computed signals from them and defining effects that react to input changes. In other words, it facilitates writing components in a reactive, signal-based style. + +#### Data Transformation + +The parent component does not need to transform or update the input data. This responsibility falls on the child component, which simplifies the logic in the parent component and makes it easier to reuse and test the child components. + +#### Change Detection + +Using signals as inputs improves the component's change detection. Angular can detect changes more efficiently because signals allow for more precise tracking of state modifications. + +#### State Derivation + +Signals enable more effective derivation of the input state. For example, you can create computed signals that depend on the inputs and automatically update the component's state when the inputs change. + +A more comprehensive article on signals in Angular can be found [here](https://blog.angular-university.io/angular-signal-inputs/). diff --git a/guides/development/angular-guidelines.md b/guides/development/angular-guidelines.md index 6f4d284..101bae2 100644 --- a/guides/development/angular-guidelines.md +++ b/guides/development/angular-guidelines.md @@ -1,39 +1,44 @@ # MarsBased Angular Style Guide +### 🚧 This guide is focused on Angular 2+, for Angular 17+ features see [here](./angular-17-guidelines.md) 🚧 + +--- + We follow the official and opinionated [Angular coding style guide](https://angular.io/guide/styleguide) as the primary source of best practices and conventions. -* 1. [Do's and Don'ts](#DosandDonts) - * 1.1. [Logic in templates](#Logicintemplates) - * 1.2. [Use index.ts](#Useindex.ts) - * 1.3. [Use CDK Virtual Scroll](#UseCDKVirtualScroll) - * 1.4. [Type Lazy modules](#TypeLazymodules) - * 1.5. [Private methods](#Privatemethods) - * 1.6. [Public methods](#Publicmethods) - * 1.7. [View Methods](#ViewMethods) - * 1.8. [Lifecycle Hooks](#LifecycleHooks) - * 1.9. [HTML Attributes](#HTMLAttributes) - * 1.10. [Empty Observables](#EmptyObservables) - * 1.11. [HostListener/HostBinding decorators versus host metadata](#HostListenerHostBindingdecoratorsversushostmetadata) - * 1.12. [Class members order](#ClassOrder) -* 2. [General project organization and architecture](#Generalprojectorganizationandarchitecture) - * 2.1. [Project structure example](#Projectstructureexample) -* 3. [Description of the most common patterns used to solve common problems](#Describemostcommonpatternsusedtosolvecommonproblems) - * 3.1. [Lazy Pages](#LazyPages) - * 3.2. [API Services](#APIServices) - * 3.2.1. [Why?](#Why) - * 3.2.2. [How?](#How) - * 3.3. [Forms (Reactive Forms)](#FormsReactiveForms) - * 3.3.1. [Simple FormBuilder sample](#SimpleFormBuildersample) - * 3.4. [Smart and Dumb (Presentational) components](#SmartandDumbPresentationalcomponents) - * 3.5. [State Management (with Akita)](#StateManagementwithAkita) - * 3.5.1. [Global Entities](#GlobalEntities) - * 3.5.2. [Specific module related logic](#Specificmodulerelatedlogic) - * 3.6. [Testing](#Testing) - * 3.7. [Memory Leaks](#MemoryLeaks) - * 3.8. [I18N](#I18N) -* 4. [Libraries](#Libraries) -* 5. [Learning Resources](#LearningResources) + +- 1. [Do's and Don'ts](#DosandDonts) + - 1.1. [Logic in templates](#Logicintemplates) + - 1.2. [Use index.ts](#Useindex.ts) + - 1.3. [Use CDK Virtual Scroll](#UseCDKVirtualScroll) + - 1.4. [Type Lazy modules](#TypeLazymodules) + - 1.5. [Private methods](#Privatemethods) + - 1.6. [Public methods](#Publicmethods) + - 1.7. [View Methods](#ViewMethods) + - 1.8. [Lifecycle Hooks](#LifecycleHooks) + - 1.9. [HTML Attributes](#HTMLAttributes) + - 1.10. [Empty Observables](#EmptyObservables) + - 1.11. [HostListener/HostBinding decorators versus host metadata](#HostListenerHostBindingdecoratorsversushostmetadata) + - 1.12. [Class members order](#ClassOrder) +- 2. [General project organization and architecture](#Generalprojectorganizationandarchitecture) + - 2.1. [Project structure example](#Projectstructureexample) +- 3. [Description of the most common patterns used to solve common problems](#Describemostcommonpatternsusedtosolvecommonproblems) + - 3.1. [Lazy Pages](#LazyPages) + - 3.2. [API Services](#APIServices) + - 3.2.1. [Why?](#Why) + - 3.2.2. [How?](#How) + - 3.3. [Forms (Reactive Forms)](#FormsReactiveForms) + - 3.3.1. [Simple FormBuilder sample](#SimpleFormBuildersample) + - 3.4. [Smart and Dumb (Presentational) components](#SmartandDumbPresentationalcomponents) + - 3.5. [State Management (with Akita)](#StateManagementwithAkita) + - 3.5.1. [Global Entities](#GlobalEntities) + - 3.5.2. [Specific module related logic](#Specificmodulerelatedlogic) + - 3.6. [Testing](#Testing) + - 3.7. [Memory Leaks](#MemoryLeaks) + - 3.8. [I18N](#I18N) +- 4. [Libraries](#Libraries) +- 5. [Learning Resources](#LearningResources) -## 1. Do's and Don'ts +## 1. Do's and Don'ts Add and follow the official [MarsBased Angular ESLint configuration](https://github.com/MarsBased/marstyle/tree/master/angular), where most of do's and dont's are already defined. -### 1.1. Logic in templates +### 1.1. Logic in templates -Don't put logic in templates. +Don't put logic in templates. ```html -
- ... -
- +
...
-
- ... -
+
...
``` ```ts @@ -73,29 +73,28 @@ export class MyComponent { } ``` -### 1.2. Use index.ts +### 1.2. Use index.ts Do use index.ts when you see the opportunity. The model directory of a module is a good candidate. It decreases the number and size of imports in components and services. Export models from the /models directory: ```ts -export * from './user.model'; -export * from './product.model'; - +export * from "./user.model"; +export * from "./product.model"; ``` Import them where you need them: ```ts -import { UserModel, ProductModel } from '../models'; +import { UserModel, ProductModel } from "../models"; ``` -### 1.3. Use CDK Virtual Scroll +### 1.3. Use CDK Virtual Scroll -Do use [CDK Virtual Scroll](https://material.angular.io/cdk/scrolling/overview) when showing huge lists of elements. It improves performance **drastically**. +Do use [CDK Virtual Scroll](https://material.angular.io/cdk/scrolling/overview) when showing huge lists of elements. It improves performance **drastically**. -### 1.4. Type Lazy modules +### 1.4. Type Lazy modules Do Type lazy modules with `async` and `Promise` types. @@ -108,11 +107,11 @@ Do Type lazy modules with `async` and `Promise` types. } ``` -### 1.5. Private methods +### 1.5. Private methods Do mark the visibility of private methods when are called only from inside the class. -### 1.6. Public methods +### 1.6. Public methods Do not mark the visibility of public methods, as it's by default. @@ -128,11 +127,11 @@ isUserVisible(): boolean { } ``` -### 1.7. View Methods +### 1.7. View Methods Do not mark as private methods called from the component's view (public). -### 1.8. Lifecycle Hooks +### 1.8. Lifecycle Hooks - Add hooks just after the class constructor. - Add their interfaces to the class. @@ -162,7 +161,7 @@ export class MyComponent implements OnInit, OnDestroy { } ``` -### 1.9. HTML Attributes +### 1.9. HTML Attributes Do sort the HTML tag attributes. @@ -175,52 +174,51 @@ Do sort the HTML tag attributes. 4. Animations 5. Static and native HTML properties. - ```html -
- ... - +
+ ... +
-
- ... - +
+ ... +
``` -### 1.10. Empty Observables +### 1.10. Empty Observables Do prefer the EMPTY variable over the of operator when generating an empty observable. ```ts // bad (Don't use `of()` operator:) -const checkActionDispatched = !isActionDispatched ? - dispatch(new Action()) : - of(); +const checkActionDispatched = !isActionDispatched + ? dispatch(new Action()) + : of(); // good -import { EMPTY } from 'rxjs'; +import { EMPTY } from "rxjs"; -const checkActionDispatched = !isActionDispatched ? - dispatch(new Action()) : - EMPTY; -``` +const checkActionDispatched = !isActionDispatched + ? dispatch(new Action()) + : EMPTY; +``` -### 1.11. HostListener/HostBinding decorators versus host metadata +### 1.11. HostListener/HostBinding decorators versus host metadata Do prefer the @HostListener and @HostBinding to the host property of the @Directive and @Component decorators. Refer to the [Angular Style Guide](https://angular.io/guide/styleguide#style-06-03) for more details. @@ -278,7 +276,7 @@ export class MyComponent { } ``` -## 2. General project organization and architecture +## 2. General project organization and architecture Follow the [Angular Style Guide](https://angular.io/guide/styleguide#style-06-03) to name files and directories. Additionally: @@ -289,7 +287,7 @@ Follow the [Angular Style Guide](https://angular.io/guide/styleguide#style-06-03 - State management files under the /state directory. - Helpers, utils and custom libraries under the /libs directory. -### 2.1. Project structure example +### 2.1. Project structure example ``` cypress/ # end-to-end tests @@ -325,9 +323,9 @@ src/app/ |- app.module.ts # root module ``` -## 3. Description of the most common patterns used to solve common problems +## 3. Description of the most common patterns used to solve common problems -### 3.1. Lazy Pages +### 3.1. Lazy Pages Lazy load every page always, placing routed modules under the /pages directory. This way, the architecture and the tree folder mirror the URL map presented to the user. @@ -338,11 +336,11 @@ Check the next guides for further information about lazy loading and feature mod - [Angular Feature Modules](https://angular.io/guide/feature-modules) - [Angular Lazy Loading](https://angular.io/guide/lazy-loading-ngmodules) -### 3.2. API Services +### 3.2. API Services We define a clear separation between API Services that retrieve data, usually from an API endpoint, from the rest of the services with business logic. -#### 3.2.1. Why? +#### 3.2.1. Why? Better control of the data flow, what happens if a request fails? should I keep the current store? should I empty the store? are we writing code only for success responses? That depends on the context, if we couple API calls and other logic we lose control of the data flow. We'll find ourselves writing the same API calls with some modifications for different situations. @@ -351,7 +349,7 @@ Better control of the data flow, what happens if a request fails? should I keep - **Independent**. API services don't have dependencies. In short, I think this could help to avoid ambiguous rules when writing services, leading to a more uniform codebase. -#### 3.2.2. How? +#### 3.2.2. How? ```ts @Injectable() @@ -393,7 +391,7 @@ export class UsersAdminService { } ``` -### 3.3. Forms (Reactive Forms) +### 3.3. Forms (Reactive Forms) The next articles are a good source to understand how to implement reactive forms in Angular: @@ -401,7 +399,7 @@ The next articles are a good source to understand how to implement reactive form - [A thorough exploration of Angular Forms](https://indepth.dev/posts/1143/a-thorough-exploration-of-angular-forms) - [Angular Reactive Forms Tips and Tricks](https://netbasal.com/angular-reactive-forms-tips-and-tricks-bb0c85400b58) -#### 3.3.1. Simple FormBuilder sample +#### 3.3.1. Simple FormBuilder sample Inject FormBuilder service to define forms fields and validations inside a component class. @@ -441,7 +439,7 @@ After, we register the created form for the HTML container tag, and attach every ``` -### 3.4. Smart and Dumb (Presentational) components +### 3.4. Smart and Dumb (Presentational) components The only goal of a presentational component is to keep the UI logic isolated. It doesn't have access to injected business services. It communicates using @Input and @Output data flows.Presentational components communicate using Input and Output only. @@ -455,13 +453,13 @@ Having a separation between smart and presentational components adds the advanta Check the [Angular University](https://blog.angular-university.io/angular-2-smart-components-vs-presentation-components-whats-the-difference-when-to-use-each-and-why/) article about smart and presentational components for a detailed explanation. -### 3.5. State Management (with Akita) +### 3.5. State Management (with Akita) Akita offers, basically, two JS classes to interact with, the store for adding/modify objects and the query for consulting it, and that's all, no more Actions, Reducers nor Effects. Akita's decoupled components: All Akita store management could be done (and it is fully recommended) throw a service so components are totally detached from it, an async method that interacts with the store remains as an async method for the component and could react to fulfilment if needed (not as flux-like store actions that are "flattened") -#### 3.5.1. Global Entities +#### 3.5.1. Global Entities It's so recommended exposing globally all entities obtained from the backend and that's the perfect use-case for the entity store and entity query , both could be imported in a root provided (or shared module imported by) service, related with 💥 Angular API Services - Development and totally in the same page, could be something like: @@ -479,7 +477,7 @@ It's so recommended exposing globally all entities obtained from the backend and - `users.query.ts`: Offers different methods for exposing store objects. Contrary to Akita recommendations, we think could be a better approach to create composed queries in users-state.service instead of here and access to the query object only throw the service. Docs users.state-service.ts: Acts as facade for any other service that want to interact with users entities, uses users-api.service to obtain users related data, fills/modifies store throw users.store and creates and exposes queries using users.query. Docs -#### 3.5.2. Specific module related logic +#### 3.5.2. Specific module related logic To code the logic related to a specific feature, all states could be imported only in the related module. In this case, Entity store/query doesn't make sense so normal store and query objects could be used. @@ -493,7 +491,7 @@ To code the logic related to a specific feature, all states could be imported on - `users-section.query.ts`: Offers different methods for exposing store objects. Contrary to Akita recommendations, we think could be a better approach to create composed queries in users-section-state.service instead of here and access to the query object only throw the service. Docs - `users-section-state.service.ts`: Holds all users-section related logic, interacts with users-section-store for filling/modifying objects, with users-section.query to expose them on demand and with api related services (users.state-service.ts) for obtaining backend data and modify/consult related store. Docs -### 3.6. Testing +### 3.6. Testing Favour end-to-end-testing over components test. @@ -501,7 +499,7 @@ Favour end-to-end-testing over components test. - Components: [Spectator](https://github.com/ngneat/spectator) and [Jest](https://jestjs.io/) - Unit JS tests: [Jest](https://jestjs.io/) -### 3.7. Memory Leaks +### 3.7. Memory Leaks Consider and prevent memory leaks from subscriptions in components. @@ -509,30 +507,27 @@ Read [Dealing with memory leaks](https://marsbased.com/es/blog/2018/06/18/dealin To avoid these leaks, we use the [NgNeat Until Destroy lib](https://github.com/ngneat/until-destroy) that adds support for unsubscribing automatically from subscriptions when a component is destroyed. - ```ts -import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; +import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; @UntilDestroy() @Component({}) export class InboxComponent { ngOnInit() { - interval(1000) - .pipe(untilDestroyed(this)) - .subscribe(); + interval(1000).pipe(untilDestroyed(this)).subscribe(); } } ``` The order of decorators is important, make sure to put @UntilDestroy() before the @Component() decorator. -### 3.8. I18N +### 3.8. I18N For simplicity and ease of use, we don't use the translation services that Angular has in its core. Use [Transloco](https://github.com/ngneat/transloco). -## 4. Libraries +## 4. Libraries - [Transloco](https://github.com/ngneat/transloco). Translation library for Angular. - [NgNeat Until Destroy lib](https://github.com/ngneat/until-destroy). Avoid memory leaks in components. @@ -544,7 +539,7 @@ Use [Transloco](https://github.com/ngneat/transloco). - [Angular Material](https://material.angular.io/). A collection of components and other Angular resources to build the UI. - [NgBootstrap](https://ng-bootstrap.github.io/#/home). Bootstrap library ported to Angular components. -## 5. Learning Resources +## 5. Learning Resources Our articles: @@ -559,8 +554,8 @@ Other resources: - [RXJS official documentation](https://rxjs-dev.firebaseapp.com/) - [Angular RxJS tutorial](https://angular.io/guide/rx-library) - [Alligator Angular's courses](https://alligator.io/angular/) -- [Angular in Depth blog](https://indepth.dev/angular) -- [Reddit Angular 2+](https://www.reddit.com/r/Angular2/) +- [Angular in Depth blog](https://indepth.dev/angular) +- [Reddit Angular 2+](https://www.reddit.com/r/Angular2/) - [Angular University](https://blog.angular-university.io) - [Angular cheatsheet](https://angular.io/guide/cheatsheet) - [Egghead](https://egghead.io/courses/for/angular) diff --git a/sections/development.md b/sections/development.md index d99f7c0..30c361b 100644 --- a/sections/development.md +++ b/sections/development.md @@ -11,3 +11,19 @@ The development is the core of our work. Here, you'll find detailed information - [Cycles and project management](/sections/development/cycles.md) - [AI tools](/sections/development/ai.md) - [Working principles](/sections/development/working-principles.md) + +## Guides + +Check out our more technical development guidelines (if you dare!). + +- [Active Record guidelines](/guides/development/activerecord-guide.md) +- [Angular guidelines](/guides/development/angular-guidelines.md) +- [Angular 17 guidelines](/guides/development/angular-17-guidelines.md) +- [Back-end guidelines](/guides/development/back-end-development-guidelines.md) +- [Code reviews guidelines](/guides/development/code-reviews-guidelines.md) +- [Coding guidelines](/guides/development/coding-guidelines.md) +- [Docker guidelines](/guides/development/docker-guide.md) +- [Git guidelines](/guides/development/git-guidelines.md) +- [React guidelines](/guides/development/react-guidelines.md) +- [Ruby & Rails guidelines](/guides/development/ruby-guidelines.md) +- [WIP: Testing guidelines](/guides/development/testing-guidelines.md) \ No newline at end of file