-
Requirements To Run Application: Docker
-
This is the foodics API
git clone https://github.com/temmyscope/food.git
-
After cloning the repo, update the
.env
file with the appropriate information, using.env.example
as guideline -
Note:
ADMIN_EMAIL
env key is the address you want all restock reminder to be sent -
***Note: the values in the
.env.example
are sufficient for running this application using the preconfigured Dockerfile; onlyAPP_KEY
needs to be updated *** -
Generate new laravel app key:
php artisan key:generate
- With Docker
# build docker
docker compose --env-file .env build
# run it
docker compose --env-file .env up -d
# Enter into container shell by running:
docker exec -it foodics sh
# run migrations within container shell
php artisan migrate;
# seed db with dataset within container shell
php artisan db:seed;
# flush queue within container shell
php artisan queue:flush
- Open (localhost)[!http://localhost:8025/] in the browser
- Run the following in the terminal while application is up:
docker logs foodics-queue-1
- Without Docker (Start Server)
php artisan octane:start --workers=auto --task-workers=auto --server=swoole --host=0.0.0.0 --port=8080
- Without Docker (Run Queue in new terminal)
php artisan queue:work --verbose --tries=3 --timeout=90
-
With Docker
- Enter into App shell in interactive session
docker exec -it foodics sh
- Run test in shell
php artisan test
-
Without Docker
php artisan test
If there are errors, please ensure migrations have run successfully first by running php artisan migrate:status
, if there are pending migrations, then run: php artisan migrate
; after which the tests can be re-run
-
see
request.http
file in the root folder to send requests -
if request was successful, it should return
order
field containing all products ordered
-
If you choose to run without using
docker-compose
and the preconfiguredDockerfile
, then the following are the system requirements needed to run this application successfully: -
PHP >= 8.1
-
Swoole PHP extension >= 4.5,with
swoole.use_shortname
set toOff
in yourphp.ini
-
Composer
-
Relational Database (MySQL preferrably, PostgresQL)
-
Redis Server
-
Our system should meet the following requirements:
-
Functional requirements:
- Creation of Order.
- Update Ingredient Stocks.
- Notify Admin Of Ingredient Qty Level (when equal to or below 50% once).
-
Non-functional requirements:
- The system should be scalable and efficient.
-
- The Application was built using the Repository Design Pattern; which follows the Clean Code Architecture quite nicely (maybe not so religiously but to a large extent).
- The Model Structure below is neither a syntactically correct PHP code nor is it the actual structure of a model; but it is provided to give a mental map of each model, its schema and the roles each field plays
class Ingredient {
id: int -> primary
name: string -> unique
initial_qty: float -> default(0) #In grams -> always convert kg to g (where 1kg -> 1000g)
available_qty: float -> default(0) #In grams -> always convert kg to g (where 1kg -> 1000g)
needs_restock: enum('true', 'false') -> default('false')
timestamp: string -> datetime #contains the updated_at & created_at fields
belongsToMany: Product
}
class Product {
id: int -> primary
name: string -> unique
timestamp: string -> datetime #contains the updated_at & created_at fields
belongsToMany: Ingredient
}
#This Model/Schema is used as an intermediate Model linking the Products and Ingredients
class ProductIngredient {
product_id: int -> index, foreign (Product)
ingredient_id: int -> index, foreign (Ingredient)
quantity: float -> default(0) #In grams -> always convert kg to g (where 1kg -> 1000g)
timestamp: string -> datetime #contains the updated_at & created_at fields
}
class Order {
id: int -> primary
timestamp: string -> datetime #contains the updated_at & created_at fields
hasManyThrough: Product, OrderItem
}
class OrderItem {
order_id: int -> index, foreign (Order)
product_id: int -> index, foreign (Product)
quantity: int -> default(1)
timestamp: string -> datetime #contains the updated_at & created_at fields
belongsTo: Order
}
-
All endpoints should return a JSON encoded data that at the least contain the accurate HTTP Response code and a status value (boolean). The exception(s) to this rule (i.e. returning status value) is/are
throw Exception
(s) - no pun intended. -
All variables make use of
snake_case
, properties, functions and method names make use ofcamelCase
; while namespaces and class names make use ofPascalCase
. -
Multi-words tables/schema/models are derived based on a
logical derivative
sense rather thanalphatical order
, as this is more convenient (in my opinion) for the sake of maintenance. E.g.product_ingredients
instead ofingredient_products
, etc.
- In a real life application where users order from their account, other fields such as
price
,user_id
, etc. would be required on theOrder
Model.
-
An assumption made on the project is that in a future extension of the project, When Admin updates stock of
Ingredients
, theneeds_restock
field will be set tofalse
and theinitial_qty
andavailable_qty
fields are updated with the new stock quantity of theIngredient
. -
I believe that some performance improvements can be done on
OrderRepository::create
method but at the moment I can't seem to wrap my head around it. -
Would be nice to use
laravel sail
but I have a preferredopenswoole/swoole
docker container, hence I would be usingdocker
anddocker compose
directly instead ofsail
-
Redis
conf
file configurations were added to prevent external connection to the redis server. -
Docker was not required in the assessment but it's the way to go if we're to reduce inconsistencies across environments and add reasonable amount of automation.
-
I used
mailpit
docker image but you can use an external provider like MailTrap and add necessary.env
values. -
While a docker configuration has been provided, the project can work outside Docker as long as there is a
redis
andMySQL
database servers available and.env
should be updated appropriately to prevent issues. -
If I were to addd
nginx
to the project, with a few environment configuration updates, the project would beproduction ready
. -
If you encounter this error
1030 Got error 168 - 'Unknown (generic) error from engine' from storage engine
while running thephp artisan migrate
, then it's a result of low storage left; first rundocker system prune -a --volumes
, then rebuild container and rerun. -
The database is persisted to the harddisk, hence if you need a fresh start when you rebuild the container, enter the app shell and run the following commands in this order:
php artisan migrate:rollback
php artisan migrate
php artisan db:seed