Introduction
The design and implementation stage follows the analysis stage. We are now in the next step in the software development process where a functional system is developed from requirements, both functional and non-functional. As Yadag follows an Agile and Incremental methodology, the specification, development and validation activities are interleaved [1:46]. Engineering decisions are made as features are rolled-out from customer interactions, requirements are gathered and re-gathered, wireframes are re-deployed through the Lovable app, and the implementation is the refactoring that follows in both the backend and frontend code base. This document will reveal the architectural design of Yadag’s Pre-Arrival Onboarding and Training Module, with a goal to reduce first-day onboarding time for Twin Creeks Greenhouse by at least 70%.
- Designing the System Architecture
The module is designed using a Layered Architecture pattern, a method for achieving separation and independence. Here, system functionality is organized into separate layers, and each layer only relies on the system directly underneath it. For Yadag, this creates a clean separation of concerns and supports requirement for maintainability and scalability.
Component Interaction
Four architectural layers are are implemented and communicate as seen on Figure 1.
- Presentation Layer (yadag-web): A React/Next.js application written in TypeScript, covering role-based access control for users (e.g., Admin, HR, and Workers). Zustand is used for client-side global state management, and React Query for server state, caching, and fetching. This ensures the UI follows responsive design patterns while avoiding the risk of cache invalidation
- Service Layer (yadag-api): The Laravel-based backend uses REST API endpoints which use Laravel Sanctum for authentication. A Service Layer Pattern is implemented such that the controllers use HTTP requests but offloads the business logic to a specific service class (e.g., PythonService.php in Figure 2). This ensures controllers are kept thin and business logic is its own separated concern [2].
- Data Layer: For local development, the Yadag engineering team uses MySQL. PostgreSQL is used in the demo and production environments hosted via AWS Virtual Private Cloud (VPC). JSON-based outputs are replaced by Laravel’s Eloquent ORM abstracting SQL dialect differences.
- OCR Layer: A Python microservice is used for uploaded Labour Market Impact Assessment (LMIA) documents, a required, legal document provided by the Government of Canada for initiating the employment of international farm workers. It uses the PyTesseract OCR engine for extracting LMIA numbers and phone numbers from custom pdf versions of this document such that the document is image-based rather than text, making data extraction nearly impossible with just a text-based file parser.
<redact_image>
Figure 2. The Complete Yadag Layered Architecture
Role-Based Access Control
Each user is assigned access rights through the service layer using a role-based access control (RBAC) design. Every request contains authentication role metadata that is validated by the Sanctum middleware prior to any controller. Workers can only access their own steps and documents; HR can manage all their workers within their assigned farm; and Admins are granted complete system access which are heavily used by the engineering team for validation and testing features prior to shipping to the demo environment, and eventually, production.
<redact_image>
Figure 3. Yadag’s Use Case Diagram
- Object-Oriented Design using the UML
PHP’s object-oriented capabilities and Laravel Eloquent’s ORM make it easier to understand and define the context and the external interactions with the Onboarding system. As well, it is helpful for designing the system architecture and developing design models. The following are the list of classes created for the Onboarding module.
- Worker: Contains worker data (e.g., name, DOB, email, phone_primary, farm_id, user_id, employee_number, insurance_number). This is a central class where other entities are derived from such as pivot tables (e.g., WorkerDocument, WorkerOnboardingItem, and WorkerDispatch).
- Onboarding: Represents a collection of Workers that is the aggregate for a worker’s onboarding progress. It owns a pivot table that joins the Worker and OnboardingItem table, thus creating a WorkerOnboardingItem which contains information such as status, completed_at, and appointment_date
- OnboardingItem: A collection of task completion items that is assigned to a Worker entity. Includes attributes such as farm_id, name, category, type, is_required, is_hiring_packet.
- ChecklistItem (Abstract): A base class defining shared task attributes (e.g., title, status, is_required) and is instantiated by the following child classes:
-
- Document: such as a file upload (e.g., passport ID, health card)
- Step: is a user action or acknowledgement (e.g., policy agreement, appointment confirmation)
5. Employment: A worker can have many employment records (1:M). This class acts as the joining entity that connects a worker to an LMIA. The fields include wage, dates, status and is the parent of Onboarding.
6. WorkerDispatch: Uses Laravel Eloquent’s polymorphic functions which illustrate inheritance and polymorphism in OO programming. This class records when HR sends a document (e.g., a “Hiring Packet”) to a worker, such as a request for a passport ID, or an employment contract that needs signature.
The relationships among these objects can be seen in Figure 4. Notice the one-to-one, one-to-many mappings from left to right. The engineering team has normalized the database to third normal form (3NF).
Object Communication
When an OnboardingItem (e.g., a hiring packet) is sent to a worker, an HTTP payload is sent from the frontend UI to the backend OnboardingItemRequest for validation, only when it passes all of the required fields (e.g., name, category, type, requirement) is it passed into the OnboardingItemController where CRUD operations are stored. A hiring packet that is sent to a worker triggers a POST request, thus creating the record in the database. The OnboardingItemResource will then provide a resource to the frontend if a user triggers a GET request such as to view the OnboardingItem, allowing it to render on the UI.
REST APIs allow the backend to send a payload back to the frontend (a response), and the frontend simply consumes the API, thus allowing it to receive data from the database so it can be rendered on the screen. Figure 5 illustrates the Onboarding system’s RESTful APIs using HTTP methods to perform CRUD operations.
<redact_image>
Figure 5. A Simple GET Request for Rendering an OnboardingItem
Designing with Software Reuse in Mind
Yadag reuses code from the object level such as a library rather than writing new code from scratch [1:213].
Frontend: shadcn/ui provides accessible UI components which uses a copy-paste model and gives the engineering team full control of styling while maintaining visual consistency across Yadag’s UI.
Backend: Laravel’s Eloquent ORM, validation pipeline, and Sanctum authentication are used heavily with every interaction to reduce security risks. Figure 5 illustrates how these authentication packages work using Authorization Bearer tokens headers and a guard that tell Laravel which user is making the request.
Production: As we are in an MVP stage, features are manually deployed to an AWS EC2 instance enclosed by a virtual private cloud (VPC). A senior engineer will need to SSH into an EC2 instance where core features are shipped to a demo environment where it is QA tested before being shipped to production. Figure 6 illustrates an AWS-based cloud infrastructure diagram containing a Nginx web server and an S3 bucket for storing large binary files and images. In the Onboading module, the S3 bucket holds text/image-based PDFs and training media (e.g., videos and presentations) up to 5GB.
<redact_image>
Figure 6. Yadag’s Cloud Infrastructure Diagram
Conclusion
Using a layered architecture, a service layer design pattern, and RBAC middleware gives the Onboarding and Training modules a secure and maintainable foundation including a separation of concerns. Our Object-Oriented approach using Laravel’s ORM and reusable frontend libraries such as React, Zustand, and Tailwind CSS are common best practices in the software development industry. This design was carefully made to achieve our 70% onboarding time reduction goal. Although the design was carefully thought-out, it was important that our team did not over-engineer too early, and that the design met the minimum requirements for a shippable MVP, a necessary step in the Twin Creeks pilot launch.
References
[1] I. Sommerville, Software Engineering, 10 Ed. Pearson Education Ltd.
[2] M. Roshandelpoor, “Service Layer Design Pattern in Laravel (PHP),” Medium, [Feb. 15, 2024]. Online: https://medium.com/@mohammad.roshandelpoor/service-layer-design-pattern-in-laravel-php-e132dcb4c2ab