Elixir-Scribe / elixir-scribe

The Elixir Scribe tool aims to help developers to embody the values of discipline and craftsmanship of the Scribes, enabling them to more easily write clean code in a clean software architecture for enhanced developer experience and productivity.
https://exadra37.com
MIT License
50 stars 2 forks source link

Feature (mix task): Add generator to scaffold the code by Domain, Resources and Actions for the core business logic #4

Open Exadra37 opened 4 months ago

Exadra37 commented 4 months ago

Mix Task to generate a folder structure organized by Domain, Resource and Actions

Github Sponsor: https://github.com/sponsors/Exadra37

Why?

To enable developers to write Clean Code in a Clean Software Architecture that respects the Single Responsibility Principle.

The traditional approach in software development tends to group all actions for a resource under the same module, which violates the Single Responsibility Principle because the module has more than one reason to change, as many as the number of actions on the resource.

What?

What to expect from this task is that when the developer executes the mix scribe.gen.domain ... command in the terminal, the result is a folder structure where a module is created for each action of a resource in a domain. This respects the Single Responsibility Principle because each module only has one reason to change: the business logic for the single action it is responsible for.

When?

The developer should use this generator whenever they need to create a new feature scoped to the core business logic. For features that also require interaction with the external world, the developer needs to use one of the other generators that will invoke this one. For example: mix scribe.gen.html, mix scribe.gen.live, mix scribe.gen.api, and others.

Acceptance Criteria

Let's base the acceptance criteria's on a developer tasked with building an Online Shop.

1. Creating a Domain Resource with the Default Actions (CRUD)

Feature: Adding the Sales Catalog

Scenario: Adding the Category resource to the Sales Catalog with the default actions (Create, Read, Update, Delete) Given that the developer is on a terminal When it executes the command mix scribe.gen.domain Sales.Catalog Category categories name:string desc:string Then it generates a folder structure with a module per each CRUD action (create, read, update and delete) for the Category resource of the Sales Catalog domain.

$ mix scribe.gen.domain Sales.Catalog Category categories name:string desc:string
...
$ tree --dirsfirst -L 6 lib/my_app
lib/my_app
├── domain
│   └── sales
│       └── catalog
│           ├── category
│           │   ├── create
│           │   │   └── create_category.ex
│           │   ├── delete
│           │   │   └── delete_category.ex
│           │   ├── edit
│           │   │   └── edit_category.ex
│           │   ├── list
│           │   │   └── list_categories.ex
│           │   ├── new
│           │   │   └── new_category.ex
│           │   ├── read
│           │   │   └── read_category.ex
│           │   ├── update
│           │   │   └── update_category.ex
│           │   └── category_schema.ex
│           └── category_api.ex
├── application.ex
├── mailer.ex
└── repo.ex

2. Creating a Domain Resource with the Default Actions (CRUD) and Extra Actions

Feature: Adding the Sales Catalog

Scenario: Adding the Product resource to the Sales Catalog with the default actions (Create, Read, Update, Delete) and Extra Actions (import, export) Given that the developer is on a terminal When it executes the command mix scribe.gen.domain Sales.Catalog Product products sku:string name:string desc:string --actions import,export Then it generates a folder structure with a module per each action (create, read, update, delete, import and export) for the Product resource of the Sales Catalog domain.

$ mix scribe.gen.domain Sales.Catalog Product products sku:string name:string desc:string --actions import,export
...
$  tree --dirsfirst -L 6 lib/my_app
lib/my_app
├── domain
│   └── sales
│       └── catalog
│           ├── product
│           │   ├── create
│           │   │   └── create_product.ex
│           │   ├── delete
│           │   │   └── delete_product.ex
│           │   ├── edit
│           │   │   └── edit_product.ex
│           │   ├── export
│           │   │   └── export_product.ex
│           │   ├── import
│           │   │   └── import_product.ex
│           │   ├── list
│           │   │   └── list_products.ex
│           │   ├── new
│           │   │   └── new_product.ex
│           │   ├── read
│           │   │   └── read_product.ex
│           │   ├── update
│           │   │   └── update_product.ex
│           │   └── product_schema.ex
│           └── product_api.ex
├── application.ex
├── mailer.ex
└── repo.ex

3. Creating a Domain Resource only with Extra Actions

Feature: Adding the Warehouse Fulfillment

Scenario: Adding the Fulfillment Order resource to the Warehouse Fulfillment Domain only with the list action Given that the developer is on a terminal When it executes the command mix scribe.gen.domain Warehouse.Fulfillment FulfillmentOrder fulfillment_orders uuid:string label:string total_quantity:integer location:string products_sku:array:string --no-default-actions --actions list Then it generates a folder structure with only one module for the extra action list.

$ mix scribe.gen.domain Warehouse.Fulfillment FulfillmentOrder fulfillment_orders uuid:string label:string total_quantity:integer location:string products_sku:array:string --no-default-actions --actions list
...
$ tree --dirsfirst -L 6 lib/my_app
lib/my_app
├── domain
│   └── warehouse
│       └── fulfillment
│           ├── fulfillment_order
│           │   ├── list
│           │   │   └── list_fulfillment_orders.ex
│           │   └── fulfillment_order_schema.ex
│           └── fulfillment_order_api.ex
├── application.ex
├── mailer.ex
└── repo.ex

Folder Structure for all Acceptance Criteria's

$ tree --dirsfirst -L 6 lib/my_app
lib/my_app
├── domain
│   ├── sales
│   │   └── catalog
│   │       ├── category
│   │       │   ├── create
│   │       │   │   └── create_category.ex
│   │       │   ├── delete
│   │       │   │   └── delete_category.ex
│   │       │   ├── edit
│   │       │   │   └── edit_category.ex
│   │       │   ├── list
│   │       │   │   └── list_categories.ex
│   │       │   ├── new
│   │       │   │   └── new_category.ex
│   │       │   ├── read
│   │       │   │   └── read_category.ex
│   │       │   ├── update
│   │       │   │   └── update_category.ex
│   │       │   └── category_schema.ex
│   │       ├── product
│   │       │   ├── create
│   │       │   │   └── create_product.ex
│   │       │   ├── delete
│   │       │   │   └── delete_product.ex
│   │       │   ├── edit
│   │       │   │   └── edit_product.ex
│   │       │   ├── export
│   │       │   │   └── export_product.ex
│   │       │   ├── import
│   │       │   │   └── import_product.ex
│   │       │   ├── list
│   │       │   │   └── list_products.ex
│   │       │   ├── new
│   │       │   │   └── new_product.ex
│   │       │   ├── read
│   │       │   │   └── read_product.ex
│   │       │   ├── update
│   │       │   │   └── update_product.ex
│   │       │   └── product_schema.ex
│   │       ├── category_api.ex
│   │       └── product_api.ex
│   └── warehouse
│       └── fulfillment
│           ├── fulfillment_order
│           │   ├── list
│           │   │   └── list_fulfillment_orders.ex
│           │   └── fulfillment_order_schema.ex
│           └── fulfillment_order_api.ex
├── application.ex
├── mailer.ex
└── repo.ex

[!TIP]
→ To only see dirs in the tree add the option -d: tree -d --dirsfirst -L 7 lib/