Skip to content

Coding Guidelines

Max Castro edited this page Nov 10, 2023 · 11 revisions

In this document, we will provide a comprehensive overview of the coding guidelines for developing the Kanvas ecosystem v1 using Laravel. We will cover best practices, troubleshoot common issues, and provide clear and concise examples to help developers implement these guidelines effectively in their projects. Additionally, for developers new to the Kanvas ecosystem and Laravel, we will provide a section that covers getting started and the basics. This document will serve as a valuable resource for developers working with the Kanvas ecosystem and Laravel, ensuring that projects are developed in a consistent, maintainable, and efficient manner.

Folder Structure

  • app: The app, located in the Kanvas ecosystem, serves as the primary interface for performing graph mutations and queries using various domains such as CRM, Inventory, Ecosystem, etc. This is where all the core functionality of the system is exposed and can be accessed by end-users.

  • database: This folder contains all of the project's database migrations, following the framework documentation outlined here.

  • config: This folder contains the configuration files for both Laravel and any plugins used in the project.

  • graphql: This folder contains the GraphQL file definitions for the project. Each domain will have its own subfolder to avoid mixing things together.

  • src: This folder contains the source code for each subsystem within the project. We will go into more detail in this document.

  • test: This folder contains all the module and app tests for the project, including unit and integration tests.

Sub Module Structure (/src)

The subsystems within the Kanvas ecosystem will follow a Domain-Driven Design (DDD) module structure, with the main folder being the Domain. Within each domain, we will have the following structure, if needed:

  • Actions: This folder contains classes responsible for performing specific actions within the domain.

  • Data (previously DataTransferObject): This folder contains classes that define the data structure and validation rules for data transfer within the domain.

  • Enums: This folder contains enumerated types used within the domain.

  • Exceptions: This folder contains exception types used within the domain.

  • Models: This folder contains classes that represent the data models for the domain.

  • Jobs: All async queuable jobs.

  • Observers: This folder contains classes that observe and respond to changes in the models within the domain.

  • Repositories: This folder contains classes that handle data storage and retrieval for the domain.

  • Services or Support: This folder contains classes that provide additional functionality and support for the domain.

  • Events: This folder the events classes for this specific domain.

By adhering to this structure, we can ensure that the subsystems are organized, consistent, and easy to maintain.

Actions vs Services

Action classes will usually be concerned with doing one particular action (hence the name), whereas a service class may have multiple methods to perform multiple actions around a particular entity.

When to use a Builder?

In the context of the Kanvas, you should consider using a builder when you need to create your custom queries, especially when working with the Lighthouse GraphQL library. Builders in this context allow you to construct tailored queries that align with the specific requirements of your application. This flexibility is particularly useful when dealing with complex data retrieval scenarios or when you need to optimize queries for performance.

Naming Classes

We will be using the same name convention as Spatie with the following changes:

  • We will always use Suffix, except for Model and DTO.

Dependency Injection and Domain Decoupling

When using Laravel within the domains of the Kanvas ecosystem, it is recommended to avoid global functions such as auth() directly within the domain logic. Instead, opt for a more explicit approach, utilizing dependency injection to pass required instances. For instance, rather than relying on auth()->user(), consider injecting the Users instance directly into the method. This enhances code readability, testability, and reduces implicit dependencies on Laravel's global functions, fostering a more modular and maintainable codebase.

Avoid:

/**
 * scopeCompany.
 *
 * @param mixed $company
 */
public function scopeFromCompany(Builder $query, mixed $company = null): Builder
{
    $company = $company instanceof Companies ? $company : auth()->user()->getCurrentCompany();
}

Preferred:

public static function userAssociatedToCompany(Companies $company, Users $user): UsersAssociatedCompanies
{
  
}

By adopting explicit dependency injection, you enhance code clarity, maintainability, and testability within your domains.