Giter Site home page Giter Site logo

lucidarch / lucid Goto Github PK

View Code? Open in Web Editor NEW
311.0 15.0 49.0 105 KB

Build scalable Laravel apps without compromising code quality and the least amount of technical debt. Effortless code navigation, onboarding and reviews. Tailored for Microservices and mindful of Monoliths.

Home Page: https://docs.lucidarch.dev

License: MIT License

Shell 3.33% PHP 96.48% Dockerfile 0.18%
lucid laravel software-architecture lucid-architecture service-oriented-architecture domain-driven-development single-responsibility-principle separation-of-concerns microservices monolith

lucid's Introduction

Documentation Slack Chat Build Status Latest Stable Version License


Table of Contents

About Lucid

Lucid is a software architecture to build scalable Laravel projects. It incorporates Command Bus and Domain Driven Design at the core, upon which it builds a stack of directories and classes to organize business logic. It also derives from SOA (Service Oriented Architecture) the notion of encapsulating functionality within a service and enriches the concept with more than the service being a class.

Use Lucid to:

  • Write clean code effortlessly
  • Protect your code from deterioriting over time
  • Review code in fractions of the time typically required
  • Incorporate proven practices and patterns in your applications
  • Navigate code and move between codebases without feeling astranged

Concept

This architecture is in an amalgamation of best practices, design patterns and proven methods.

  • Command Bus: to dispatch units of work. In Lucid terminology these units will be a Feature, Job or Operation.
  • Domain Driven Design: to organize the units of work by categorizing them according to the topic they belong to.
  • Service Oriented Architecture: to encapsulate and manage functionalities of the same purpose with their required resources (routes, controllers, views, datatbase migrations etc.)

If you prefer a video, watch the announcement at Laracon EU 2016:


Table of Contents

Position

In a typical MVC application, Lucid will be the bond between the application's entrypoints and the units that do the work, securing code form meandring in drastic directions:

Lucid MVC Position

The Stack

At a glance...

Lucid Stack

Framework

Provides the "kernel" to do the heavy lifting of the tedious stuff such as request/response lifecycle, dependency injection, and other core functionalities.

Foundation

Extends the framework to provide higher level abstractions that are custom to the application and can be shared across the entire stack rather than being case-specific.

Examples of what could go into foundation are:

  • DateTime a support class for common date and time functions
  • JsonSerializableInterface that is used to identify an object to be serializable from and to JSON format

Domains

Provide separation to categorize jobs and corresponding classes that belong to the same topic. A domain operates in isolation from other domains and exposes its functionalities to features and operations through Lucid jobs only.

Consider the structure below for an example on what a domain may look like:

app/Domains/GitHub
├── GitHubClient
├── Jobs
│   ├── FetchGitHubRepoInfoJob
│   └── LoginWithGitHubJob
├── Exceptions
│   ├── InvalidTokenException
│   └── RepositoryNotFoundException
└── Tests
    └── GitHubClientTest
    └── Jobs
        ├── FetchGitHubReposJobTest
        └── LoginWithGitHubJobTest

documentation contains more details on working with domains.

Services

Are directories rich in functionality, used to separate a [Monolith]({{<ref "/micro-vs-monolith/#monolith">}}) into areas of focus in a multi-purpose application.

Consider the example of an application where we enter food recipes and would want our members to have discussions in a forum, we would have two services: 1) Kitchen, 2) Forum where the kitchen would manage all that's related to recipes, and forum is obvious:

app/Services
├── Forum
└── Kitchen

and following is a single service's structure, highlighted are the Lucid specific directories:

app/Services/Forum
├── Console
│   └── Commands
├── Features
├── Operations
├── Http
│   ├── Controllers
│   └── Middleware
├── Providers
│   ├── KitchenServiceProvider
│   ├── BroadcastServiceProvider
│   └── RouteServiceProvider
├── Tests
│   └── Features
│   └── Operations
├── database
│   ├── factories
│   ├── migrations
│   └── seeds
├── resources
│   ├── lang
│   └── views
└── routes
    ├── api
    ├── channels
    ├── console
    └── web

documentation has more examples of services and their contents.

Features

Represent a human-readable application feature in a class. It contains the logic that implements the feature but with the least amount of detail, by running jobs from domains and operations at the application or service level.

Serving the Feature class will be the only line in a controller's method (in MVC), consequently achieving the thinnest form of controllers.

class AddRecipeFeature extends Feature
{
    public function handle(AddRecipe $request)
    {
        $price = $this->run(CalculateRecipePriceOperation::class, [
            'ingredients' => $request->input('ingredients'),
        ]);

        $this->run(SaveRecipeJob::class, [
            'price' => $price,
            'user' => Auth::user(),
            'title' => $request->input('title'),
            'ingredients' => $request->input('ingredients'),
            'instructions' => $request->input('instructions'),
        ]);

        return $this->run(RedirectBackJob::class);
    }
}

documentation about features expands on how to serve them as classes from anywhere.

Operations

Their purpose is to increase the degree of code reusability by piecing jobs together to provide composite functionalities from across domains.

class NotifySubscribersOperation extends Operation
{
    private int $authorId;

    public function __construct(int $authorId)
    {
        $this->authorId = $authorId;
    }

    /**
     * Sends notifications to subscribers.
     *
     * @return int Number of notification jobs enqueued.
     */
    public function handle(): int
    {
        $author = $this->run(GetAuthorByIDJob::class, [
            'id' => $this->authorId,
        ]);

        do {

            $result = $this->run(PaginateSubscribersJob::class, [
                'authorId' => $this->authorId,
            ]);

            if ($result->subscribers->isNotEmpty()) {
                // it's a queueable job so it will be enqueued, no waiting time
                $this->run(SendNotificationJob::class, [
                    'from' => $author,
                    'to' => $result->subscribers,
                    'notification' => 'article.published',
                ]);
            }

        } while ($result->hasMorePages());

        return $result->total;
    }
}

documentation goes over this simple yet powerful concept.

Data

For a scalable set of interconnected data elements, we've created a place for them in app/Data, because most likely over time writing the application there could develop a need for more than Models in data, such as Repositories, Value Objects, Collections and more.

app/Data
├── Models
├── Values
├── Collections
└── Repositories

Benefits

There are valuable advantages to what may seem as overengineering.

Organization

  • Predictable impact of changes on the system when reviewing code
  • Reduced debugging time since we’re dividing our application into isolated areas of focus (divide and conquer)
  • With Monolith, each of our services can have their own versioning system (e.g. Api service is at v1 while Chat is at v2.3 yet reside) yet reside in the same codebase

Reuse & Replace

By dissecting our application into small building blocks of code - a.k.a units - we've instantly opened the door for a high degree of code sharing across the application with Data and Domains, as well as replaceability with the least amount of friction and technical debt.

Boundaries

By setting boundaries you would've taken a step towards proetcting application code from growing unbearably large and made it easier for new devs to onboard. Most importantly, that you've reduced technical debt to the minimum so that you don't have to pay with bugs and sleepless nights; code doesn't run on good intentions nor wishes.

Multitenancy

When our application scales we'd typically have a bunch of instances of it running in different locations, at some point we would want to activate certain parts of our codebase in some areas and shut off others.

Here’s a humble example of running Api, Back Office and Web App instances of the same application, which in Lucid terminology are services that share functionality through data and domains:

Lucid multitenancy

Contribute

Bug & Issue Reports

To encourage active collaboration, Lucid strongly encourages contribution through pull requests. "Bug reports" may be searched or created in issues or sent in the form of a pull request containing a failing test or steps to reproduce the bug.

If you file a bug report, your issue should contain a title and a clear description of the issue. You should also include as much relevant information as possible and a code sample that demonstrates the issue. The goal of a bug report is to make it easy for yourself - and others - to replicate the bug and develop a fix.

⏱ PRs and issues are usually checked about three times a week so there is a high chance yours will be picked up soon.

The Lucid Architecture source code is on GitHub as lucidarch/lucid.

Support Questions

Lucid Architecture's GitHub issue trackers are not intended to provide help or support. Instead, use one of the following channels:

  • Discussions is where most conversations takes place
  • For a chat hit us on our official Slack workspace in the #support channel
  • If you prefer StackOverflow to post your questions you may use #lucidarch to tag them

Core Development Discussion

You may propose new features or improvements of existing Lucid Architecture behaviour in the Lucid Discussins. If you propose a new feature, please be willing to implement at least some of the code that would be needed to complete the feature, or collaborate on active ideation in the meantime.

Informal discussion regarding bugs, new features, and implementation of existing features takes place in the #internals channel of the Lucid Slack workspace. Abed Halawi, the maintainer of Lucid, is typically present in the channel on weekdays from 8am-5pm EEST (Eastern European Summer Time), and sporadically present in the channel at other times.

Which Branch? And How To Contribute

The main branch is what contains the latest live version and is the one that gets released.

  • Fork this repository
  • Clone the forked repository to where you'll edit your code
  • Create a branch for your edits (e.g. feature/queueable-units, fix/issue-31)
  • Commit your changes and their tests (if applicable) with meaningful short messages
  • Push your branch git push origin feature/queueable-units
  • Open a PR to the main branch, which will run tests for your edits

⏱ PRs and issues are usually checked about three times a week.

Setup for Development

Following are the steps to setup for development on Lucid:

Assuming we're in ~/dev directory...

  • Clone the forked repository [your username]/lucid which will create a lucid folder at ~/dev/lucid
  • Create a Laravel project to test your implementation in it composer create-project laravel/laravel myproject
  • Connect the created Laravel project to the local Lucid installation; in the Laravel project's composer.json
    "require": {
        "...": "",
        "lucidarch/lucid": "@dev"
    },
    "repositories": [
        {
            "type": "path",
            "url": "~/dev/lucid",
            "options": {
                "symlink": true
            }
        }
    ],
    "minimum-stability": "dev",

Make sure you change the url to the absolute path of your directory

  • Run composer update to create the symlink

Now all your changes in the lucid directory will take effect automatically in the project.

Security Vulnerabilities

If you discover a security vulnerability within Lucid, please send an email to Abed Halawi at [email protected]. All security vulnerabilities will be promptly addressed.

Coding Style

Lucid Architecture follows the PSR-2 coding standard and the PSR-4 autoloading standard.

PHPDoc

Below is an example of a valid Lucid Architecture documentation block. Note that the @param attribute is followed by two spaces, the argument type, two more spaces, and finally the variable name:

/**
 * Register a binding with the container.
 *
 * @param  string|array  $abstract
 * @param  \Closure|string|null  $concrete
 * @param  bool  $shared
 * @return void
 *
 * @throws \Exception
 */
public function bind($abstract, $concrete = null, $shared = false)
{
    //
}

Code of Conduct

The Lucid Architecture code of conduct is derived from the Laravel code of conduct. Any violations of the code of conduct may be reported to Abed Halawi ([email protected]):

  • Participants will be tolerant of opposing views.
  • Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
  • When interpreting the words and actions of others, participants should always assume good intentions.
  • Behavior that can be reasonably considered harassment will not be tolerated.

lucid's People

Contributors

batisska avatar illiabalia avatar kohlerdominik avatar mulkave avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lucid's Issues

Factories / Seeders in Service deprecated?

Is it deprecated to make factories and seeders in services?
There's no command for making them for a service and since factories and seeders have namespaces now, it would require registering them in the service's provider which is ALSO not being done.

I feel like it should be properly decided and updated whether services should have separate database folders or share the laravel project's and maybe just have folders there.

Integrating Laravel-Lighthouse

Hi there, I want to know how can u integrate Laravel-lighthouse with lucid. If you can guide me that will be much appreciated.

Wrong service provider mentioned in the documentation

In the service structure example under the "Concept -> Services" you defined the wrong service provider. Instead of KitchenServiceProvider, you should define ForumServiceProvider.

 app/Services/Forum
├── Console
│   └── Commands
├── Features
├── Operations
├── Http
│   ├── Controllers
│   └── Middleware
├── Providers
│   ├── KitchenServiceProvider <- should be ForumServiceProvider
...

Under the "Concept -> Organization -> Last point" there is a duplicated phrase "yet reside".


There is a typo under the "Concept -> Boundaries". Word "proetcting" should become "protecting".

Issue creating a service on Monolith

After installing composer require lucidarch/lucid and initializing lucid init:monolith, creating a service lucid make:service Web display an error.

App namespace not set in composer.json
....\vendor\lucidarch\lucid\src\Finder.php at 143

But seems like the service is generated though.

image

Issue with Job in Laravel 10x

When I updated the laravel framework to 10x, the application is totally broken. Meaning instead of returning what it did before, it now just returning Illuminate\Foundation\Bus\PendingDispatch. Is there any plan to make this okay in future or not?

Laravel 9 Support?

I haven't tried it yet, but is it confirmed that Lucid supports Laravel 9 yet?

Laravel 10 upgrade

In laravel 10, I think Jobs now respond with Illuminate\Foundation\Bus\PendingDispatch objects rather than the response of what the handle method has.

In feature:

$this->run(GenerateUUIDJob::class);

now responds with

Illuminate\Foundation\Bus\PendingDispatch^ {#9196 // app/Models/Traits/HasUuid.php:36
  #job: App\Domains\Uid\Jobs\GenerateUUIDJob^ {#9197}
  #afterResponse: false
}

instead of the response of what the handle method does:

class GenerateUUIDJob extends Job
{

    public function __construct()
    {
    }

    public function handle(): string
    {
        return Str::orderedUuid();
    }
}

How to handle a feature that has related jobs

The architecture seems incredible to me. I'm going to start using it. Thanks for the input. After reading the documentation I have doubts in relation to the characteristics. I am on the example of creating an article. I want to divide the process of creating an article into 4 jobs:

  1. Validate data
  2. Upload image to an amazon bucket
  3. Save the post
  4. Return the answer

Suppose that step 1 and 2 are executed worse when reaching three an error occurs, you would have to make a rollback of step 2 and delete the uploaded file.

Question 1: Would I have to make a job run if one of these steps cannot be created, something like DeleteImageJob?
Question 2: How would you execute an Eloquent transaction in this process?

Again, congratulations on the job. Greetings

Features not working with Laravel 9

Hi, when calling $this->serve(new AbcFeature()); this error occurs:

Illegal offset type in isset or empty

In file vendor / laravel / framework / src / Illuminate / Container / Container.php : 1285

error

I tried with trait Lucid\Bus\ServesFeatures and with controller Lucid\Units\Controller, both ways produce the error.

Feature alone works:

new AbcFeature(); // ok
(new AbcFeature())->handle(); // ok

PHP 8.1.3
Laravel 9.16.0

Is this project dead??

This project has not been updated for the last six months and consequently no support for the Laravel 10.

If the maintainers of this project are not willing to support any future updates, they should mark and declare this project is deprecated. So future open-sourcer can take over and continue the work of this project.

Handling exception

Hello, what do you recommend for handling exceptions in function or operation?
I think I should launch it at Job and capture in the Features, Operation Or Controller, but I don't know if it is recommended.
Can you give me a suggestion?

(Custom) stubs

I am making stubs as I want to include a copyright tag in each file and because I am migrating from PHPunit to PEST and want to change the test stubs to use PEST. I have made stubs in the stubs directory and Artisan recognises them but Lucid doesn't (E.G model.stub). I have looked into the source code and see that Lucid only reads its own stubs, could it be changed so that it also reads from the stubs folder?

Calling a job from another service

I am trying to write an application with micro service using lucid. However I have quite a problem getting which code I need to put in the job from a Service that call a job from another service.

Do I need to repeat the same code or is there something to writte inside it that send it directly to the queue?

Sorry by advance if the question seems stupid but I didn't found my answer by reading the documentation.

Deprecated code on `lucid init:micro`

PHP 8.1, Laravel 9

I just added Lucid to my project and ran lucid init:micro and got this:

Initializing Lucid Micro for Laravel 9.0.0

Created directories:
/app/Data
/app/Domains
/app/Features
/app/Operations
/app/Data/Models
/tests/Domains
/tests/Operations
PHP Deprecated:  str_replace(): Passing null to parameter #3 ($subject) of type array|string is deprecated in /var/www/html/vendor/laravel/
framework/src/Illuminate/Support/Str.php on line 637

Deprecated: str_replace(): Passing null to parameter #3 ($subject) of type array|string is deprecated in /var/www/html/vendor/laravel/frame
work/src/Illuminate/Support/Str.php on line 637

You're all set to build something awesome that scales!

Here are some examples to get you started:

You may wish to start with a feature
lucid make:feature LoginUser
will generate app/Features/LoginUserFeature.php

Or a job to do a single thing
lucid make:job GetUserByEmail User
will generate app/Domains/User/Jobs/GetUserByEmailJob.php

For more Job examples check out Lucid's built-in jobs:
- Lucid\Domains\Http\Jobs\RespondWithJsonJob
for consistent JSON structure responses.

- Lucid\Domains\Http\Jobs\RespondWithJsonErrorJob
for consistent JSON error responses.

- Lucid\Domains\Http\Jobs\RespondWithViewJob
basic view and data response functionality.

Finally you can group multiple jobs in an operation
lucid make:operation ProcessUserLogin
will generate app/Operations/ProcessUserLoginOperation.php

For more details, help yourself with the docs at https://docs.lucidarch.dev

Remember to enjoy the journey.
Cheers!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.