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 HTML generator to scaffold the code by Domain, Resources and Actions for the Web layer #5

Open Exadra37 opened 4 months ago

Exadra37 commented 4 months ago

Mix Task to generate the Web 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.html ... command in the terminal, the result is a folder structure where a module is created for each action of a resource in a domain at lib/my_app_web/. This respects the Single Responsibility Principle because each module only has one reason to change: the web logic for the single action it is responsible for.

This generator will also invoke mix scribe.gen.domain (issue #4) to generate the Domain, Resource and Actions for the core business logic.

When?

The developer should use this generator whenever they need to create a new feature consumed by a web app via HTML endpoints.

Acceptance Criteria's

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) for the HTML and Core layers

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.html 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 at lib/my_app_web and lib/my_app.

$ mix scribe.gen.html Sales.Catalog Category categories name:string desc:string
...
$ tree --dirsfirst -L 6 lib/my_app_web/domain
lib/my_app_web/domain
└── sales
    └── catalog
        └── category
            ├── create
            │   └── create_category_controller.ex
            ├── delete
            │   └── delete_category_controller.ex
            ├── edit
            │   ├── edit_category.html.heex
            │   └── edit_category_controller.ex
            ├── list
            │   ├── list_categories.html.heex
            │   └── list_categories_controller.ex
            ├── new
            │   ├── new_category.html.heex
            │   └── new_category_controller.ex
            ├── read
            │   ├── read_category.html.heex
            │   └── read_category_controller.ex
            ├── update
            │   └── update_category_controller.ex
            ├── category_form.html.heex
            └── category_html.ex

2. Creating a Domain Resource with the Default Actions (CRUD) and Extra Actions for the HTML and Core layers

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.html 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 at lib/my_app_web and lib/my_app.

$ mix scribe.gen.html Sales.Catalog Product products sku:string name:string desc:string --actions import,export
...
$  tree --dirsfirst -L 6 lib/my_app_web/domain
lib/my_app_web/domain
└── sales
    └── catalog
        └── product
            ├── create
            │   └── create_product_controller.ex
            ├── delete
            │   └── delete_product_controller.ex
            ├── edit
            │   ├── edit_product.html.heex
            │   └── edit_product_controller.ex
            ├── export
            │   └── default_product_controller.ex
            ├── import
            │   └── default_product_controller.ex
            ├── list
            │   ├── list_products.html.heex
            │   └── list_products_controller.ex
            ├── new
            │   ├── new_product.html.heex
            │   └── new_product_controller.ex
            ├── read
            │   ├── read_product.html.heex
            │   └── read_product_controller.ex
            ├── update
            │   └── update_product_controller.ex
            ├── product_form.html.heex
            └── product_html.ex

3. Creating a Domain Resource only with Extra Actions for the HTML and COre layers

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.html 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 at lib/my_app_web and lib/my_app.

$ mix scribe.gen.html Warehouse.Fulfillment FulfillmentOrder fulfillment_orders uuid:string label:string total_quantity:integer location:string products_sku:array:string --no-default-actions --actions list
...
$  lib/my_app_web/domain
└── warehouse
    └── fulfillment
        └── fulfillment_order
            ├── list
            │   ├── list_fulfillment_orders.html.heex
            │   └── list_fulfillment_orders_controller.ex
            ├── fulfillment_order_form.html.heex
            └── fulfillment_order_html.ex

Folder Structure for all Acceptance Criteria's

$  tree --dirsfirst -L 7 lib/
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
├── my_app_web
│   ├── components
│   │   ├── layouts
│   │   │   ├── app.html.heex
│   │   │   └── root.html.heex
│   │   ├── core_components.ex
│   │   └── layouts.ex
│   ├── controllers
│   │   ├── page_html
│   │   │   └── home.html.heex
│   │   ├── error_html.ex
│   │   ├── error_json.ex
│   │   ├── page_controller.ex
│   │   └── page_html.ex
│   ├── domain
│   │   ├── sales
│   │   │   └── catalog
│   │   │       ├── category
│   │   │       │   ├── create
│   │   │       │   │   └── create_category_controller.ex
│   │   │       │   ├── delete
│   │   │       │   │   └── delete_category_controller.ex
│   │   │       │   ├── edit
│   │   │       │   │   ├── edit_category.html.heex
│   │   │       │   │   └── edit_category_controller.ex
│   │   │       │   ├── list
│   │   │       │   │   ├── list_categories.html.heex
│   │   │       │   │   └── list_categories_controller.ex
│   │   │       │   ├── new
│   │   │       │   │   ├── new_category.html.heex
│   │   │       │   │   └── new_category_controller.ex
│   │   │       │   ├── read
│   │   │       │   │   ├── read_category.html.heex
│   │   │       │   │   └── read_category_controller.ex
│   │   │       │   ├── update
│   │   │       │   │   └── update_category_controller.ex
│   │   │       │   ├── category_form.html.heex
│   │   │       │   └── category_html.ex
│   │   │       └── product
│   │   │           ├── create
│   │   │           │   └── create_product_controller.ex
│   │   │           ├── delete
│   │   │           │   └── delete_product_controller.ex
│   │   │           ├── edit
│   │   │           │   ├── edit_product.html.heex
│   │   │           │   └── edit_product_controller.ex
│   │   │           ├── export
│   │   │           │   └── default_product_controller.ex
│   │   │           ├── import
│   │   │           │   └── default_product_controller.ex
│   │   │           ├── list
│   │   │           │   ├── list_products.html.heex
│   │   │           │   └── list_products_controller.ex
│   │   │           ├── new
│   │   │           │   ├── new_product.html.heex
│   │   │           │   └── new_product_controller.ex
│   │   │           ├── read
│   │   │           │   ├── read_product.html.heex
│   │   │           │   └── read_product_controller.ex
│   │   │           ├── update
│   │   │           │   └── update_product_controller.ex
│   │   │           ├── product_form.html.heex
│   │   │           └── product_html.ex
│   │   └── warehouse
│   │       └── fulfillment
│   │           └── fulfillment_order
│   │               ├── list
│   │               │   ├── list_fulfillment_orders.html.heex
│   │               │   └── list_fulfillment_orders_controller.ex
│   │               ├── fulfillment_order_form.html.heex
│   │               └── fulfillment_order_html.ex
│   ├── endpoint.ex
│   ├── gettext.ex
│   ├── router.ex
│   └── telemetry.ex
├── my_app.ex
└── my_app_web.ex

[!TIP]
→ To only see the Core layer run: tree --dirsfirst -L 6 lib/my_app
→ To see both the Web and Core layer together run: tree --dirsfirst -L 7 lib → To only see dirs in the tree add the option -d: tree -d --dirsfirst -L 7 lib/