Repository
https://github.com/creatrixity/adonis-hexa
New Project: Adonis Hexa
What is Adonis Hexa about?
Adonis Hexa is a software development paradigm for maintaining a scalable application architecture while developing apps for the AdonisJS framework.
The main reason we use Adonis Hexa is to reduce the amount of technical debt incurred over the lifetime of a project. By providing a stable, reliable set of architectural patterns, we are able to assure that our codebase is really determinist and straightforward.
Technology Stack
- AdonisJS: >=4.1.0
- Node.js
- Chai.js
Roadmap
- Develop Adonis Hexa core—Create a stable base blueprint for future contributions to Adonis Hexa.
- Create a CLI generator—To greatly ease development experience with Adonis Hexa, a command line interface will be developed to execute mundane, repetitive tasks.
- Create an NPM module—We plan to create an NPM module for Adonis Hexa within the next iterations.
- Create a visual user interface accessible over the web.
How to contribute?
Found a bug? You may raise an issue. Created a fix for a bug you encountered? Please make a pull request. Want to talk? Contact me via creatrixity@gmail.com or post a tweet @creatrixity
GitHub Account
https://github.com/creatrixity
How do I use Adonis Hexa for my next AdonisJS project?
Simply head over to https://github.com/creatrixity/adonis-hexa and clone the repo as it provides a stable base to get you started.
git clone https://github.com/creatrixity/adonis-hexa.git
...then run npm install
.
Then run the following command to run startup migrations.
adonis migration:run
Exploring building a feature with Adonis Hexa
To explore a genuine real-world use case for Adonis Hexa, we'll be leveraging Adonis Hexa to add the search feature to an application that requires us to be able to find users by sending data to an API endpoint. After carrying out our installation steps. We'll add the adonis-search
package to help with building out our search feature. Add this line to the dependencies
object in package.json
at the root.
"dependencies": {
"adonis-search": "^1.0.3",
// ....
}
We'll also add its service provider to our service providers. Add this service provider to start/app.js
.
const providers = [
"adonis-search/providers/QueryProvider"
//...
];
We then install all dependencies.
npm install
We'll need to add a search route available at /users/search
. Add this route to src/Services/Api/Routes.js
.
{
route: "users/search",
controller: "UserController.getSearchUsers",
method: "get"
},
The Hexa way to do routing is a little different from regular Adonis routes. Our controller UserController.getSearchUsers
means we are using the getSearchUsers
method of the UserController
class and we are handling GET
requests.
We'll add the getSearchUsers
method to the UserController
.
getSearchUsers ({ request, params }) {
return this.serve('Api/Features/SearchUsersFeature', {
request
});
}
This is a one-liner method that simply "runs" the SearchUsersFeature
. The SearchUsersFeature
will assemble the jobs that do the actual user searching. We'll create src/Services/Api/Features/SearchUsersFeature.js
.
"use strict";
const BaseFeature = use("Src/Foundation/BaseFeature");
const UserRepository = use("Src/Data/Repositories/UserRepository");
/**
* Searches through and returns users with their username or email matching provided queries.
*
* **Namespace**: `Src/Services/Api/Features/SearchUsersFeature`
*
* @class SearchUsersFeature
*/
class SearchUsersFeature extends BaseFeature {
constructor(params) {
super(params);
}
/**
* Contains code that will be ran when this feature is invoked.
*
* @method handle
*
* @return {Object} JSON
*/
async handle() {
const { request } = this.params;
const query = await this.run("User/Jobs/CreateUserSearchQueryJob", {
request
});
const users = await this.run("User/Jobs/RetrieveUsersJob", {
query: query.search(["username", "email"])
});
return {
users
};
}
}
module.exports = SearchUsersFeature;
We get the request
object (available within this.params
in all Hexa controllers) and then pass it to the CreateUserSearchQueryJob
. This job will create a search query object that we'll pass to the RetrieveUsersJob
as a query. The RetrieveUsersJob
will return users matching the search query provided.
Let's create src/Domains/User/Jobs/CreateUserSearchQueryJob.js
.
"use strict";
const BaseJob = use("Src/Foundation/BaseJob");
const UserRepository = use("Src/Data/Repositories/UserRepository");
const Query = use("Query");
/**
* Creates a user search query.
*
* **Namespace**: `Src/Domains/User/CreateUserSearchQueryJob`
* **Singleton**: No
* **Alias**: None
*
* @class CreateUserSearchQueryJob
* @constructor
*/
class CreateUserSearchQueryJob extends BaseJob {
constructor(params) {
super(params);
}
/**
* Contains code that will be ran when this job is invoked.
*
* @method handle
*
* @return {Object} Lucid/ORM
*/
async handle() {
const query = new Query(this.params.request, {
order: "id"
});
return query;
}
}
module.exports = CreateUserSearchQueryJob;
We use the adonis-search
package to process our search query. We make sure our search results are ordered according to their id
property.
Let's create src/Domains/User/Jobs/RetrieveUsersJob.js
.
"use strict";
const BaseJob = use("Src/Foundation/BaseJob");
const UserRepository = use("Src/Data/Repositories/UserRepository");
/**
* Retrieves users matching query.
*
* **Namespace**: `Src/Domains/User/RetrieveUsersJob`
*
* @class RetrieveUsersJob
* @constructor
*/
class RetrieveUsersJob extends BaseJob {
constructor(params) {
super(params);
}
/**
* Contains code that will be ran when this job is invoked.
*
* @method handle
*
* @return {Object} Lucid/ORM
*/
async handle() {
const { query } = this.params;
const userRepo = new UserRepository();
const users = await userRepo.pageWhere(query);
return users;
}
}
module.exports = RetrieveUsersJob;
We use the pageWhere
method freely available to us thanks to the Repository
class that comes with Hexa. This allows us to return results in pages just like Google would.
We've now been able to add a completely reusable feature alongside two completely reusable jobs. This is the power of Adonis Hexa.
Testing approach.
We leverage Chai
assertions for testing which are compatible with AdonisJS test runners. Read this article to learn more.
So, what can we create with Adonis Hexa?
Adonis Hexa allows you to speed up your coding cycles by keeping code reusable. Let your imagination run wild and go creative with it.
Below is the screenshot of an app with the SearchUsersFeature
reused to find items for a fast food ordering app.