diff --git a/src/Project/service.go b/src/Project/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..03691430ffd83bab2f88810799d2e545ad34cac4
--- /dev/null
+++ b/src/Project/service.go
@@ -0,0 +1,176 @@
+package project
+
+import (
+ "context"
+ "fmt"
+ "log"
+
+ "github.com/google/uuid"
+ "go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+// ProjectService contains the business logic for project management operations
+// This service layer sits between the web controllers and the data repository
+// It enforces business rules, handles validation, and coordinates data operations
+type ProjectService struct {
+ repo ProjectRepository // Repository interface for data access operations
+}
+
+// NewProjectService creates and initializes a new ProjectService instance
+// The service requires a repository implementation to handle data persistence
+func NewProjectService(repo ProjectRepository) *ProjectService {
+ log.Println("INFO: ProjectService initialized")
+ return &ProjectService{repo: repo}
+}
+
+// CreateProject handles the creation of new projects with business logic validation
+// This method ensures project uniqueness, validates business rules, and creates the project
+func (s *ProjectService) CreateProject(ctx context.Context, toCreate *ProjectToCreate) (*Project, error) {
+ log.Printf("INFO: Creating project with ID '%s' and name '%s'", toCreate.ProjectID, toCreate.Name)
+
+ // Business Rule 1: Project IDs must be unique across the entire system
+ // Check if a project with this ID already exists before creating a new one
+ log.Printf("DEBUG: Checking if project ID '%s' already exists", toCreate.ProjectID)
+ existing, err := s.repo.FindByProjectID(ctx, toCreate.ProjectID)
+ if err == nil && existing != nil {
+ // If we found an existing project, the creation should fail
+ log.Printf("WARN: Project creation failed - ID '%s' already exists", toCreate.ProjectID)
+ return nil, fmt.Errorf("project with ID '%s' already exists", toCreate.ProjectID)
+ }
+
+ // Business Rule 2: Project end date must be after the start date
+ // This prevents creating projects with invalid date ranges
+ if toCreate.EndDate.Before(toCreate.StartDate) {
+ log.Printf("WARN: Project creation failed - end date (%s) before start date (%s)",
+ toCreate.EndDate.Format("2006-01-02"), toCreate.StartDate.Format("2006-01-02"))
+ return nil, fmt.Errorf("end date must be after start date")
+ }
+
+ log.Printf("DEBUG: Project validation passed, creating in repository")
+
+ // All business rules passed, delegate to repository for actual creation
+ project, err := s.repo.Create(ctx, toCreate)
+ if err != nil {
+ log.Printf("ERROR: Failed to create project '%s' in repository: %v", toCreate.ProjectID, err)
+ return nil, err
+ }
+
+ log.Printf("INFO: Project '%s' created successfully with MongoDB ID %s",
+ project.ProjectID, project.ID.Hex())
+ return project, nil
+}
+
+// GetProjectsByUser retrieves all projects belonging to a specific user
+// This method provides a simple interface for fetching user-specific projects
+func (s *ProjectService) GetProjectsByUser(ctx context.Context, userID uuid.UUID) ([]*Project, error) {
+ log.Printf("INFO: Getting projects for user %s", userID)
+
+ // Delegate to repository for data retrieval
+ projects, err := s.repo.FindByCreatedBy(ctx, userID)
+ if err != nil {
+ log.Printf("ERROR: Failed to get projects for user %s: %v", userID, err)
+ return nil, err
+ }
+
+ log.Printf("INFO: Found %d projects for user %s", len(projects), userID)
+
+ // Debug logging: Show project names for troubleshooting
+ if len(projects) > 0 {
+ log.Printf("DEBUG: Projects found: %v", func() []string {
+ names := make([]string, len(projects))
+ for i, p := range projects {
+ names[i] = p.Name
+ }
+ return names
+ }())
+ }
+
+ return projects, nil
+}
+
+// GetProjectWithRequirements retrieves a project along with all its associated requirements
+// This method is optimized for displaying project detail pages where both
+// project information and requirements are needed simultaneously
+func (s *ProjectService) GetProjectWithRequirements(ctx context.Context, id primitive.ObjectID) (*ProjectWithRequirements, error) {
+ log.Printf("INFO: Getting project with requirements for ID %s", id.Hex())
+
+ // Use the repository method that efficiently loads project and requirements together
+ // This is more efficient than making separate calls for project and requirements
+ projectWithReqs, err := s.repo.FindWithRequirements(ctx, id)
+ if err != nil {
+ log.Printf("ERROR: Failed to get project with requirements for ID %s: %v", id.Hex(), err)
+ return nil, err
+ }
+
+ log.Printf("INFO: Loaded project '%s' with %d requirements",
+ projectWithReqs.Project.Name, projectWithReqs.RequirementCount)
+
+ return projectWithReqs, nil
+}
+
+// UpdateProject handles project updates with business rule validation
+// This method ensures that updates maintain data integrity and business rules
+func (s *ProjectService) UpdateProject(ctx context.Context, update *ProjectToUpdate) (*Project, error) {
+ log.Printf("INFO: Updating project %s with name '%s'", update.ID.Hex(), update.Name)
+
+ // Business Rule: End date must still be after start date after the update
+ // This validation is applied to updates just like it is for creation
+ if update.EndDate.Before(update.StartDate) {
+ log.Printf("WARN: Project update failed - end date (%s) before start date (%s)",
+ update.EndDate.Format("2006-01-02"), update.StartDate.Format("2006-01-02"))
+ return nil, fmt.Errorf("end date must be after start date")
+ }
+
+ log.Printf("DEBUG: Project update validation passed, updating in repository")
+
+ // Delegate to repository for the actual update operation
+ project, err := s.repo.Update(ctx, update)
+ if err != nil {
+ log.Printf("ERROR: Failed to update project %s: %v", update.ID.Hex(), err)
+ return nil, err
+ }
+
+ log.Printf("INFO: Project '%s' updated successfully", project.Name)
+ return project, nil
+}
+
+// DeleteProject handles the complete removal of a project and all associated data
+// This method ensures that all related requirements are also deleted to maintain data consistency
+func (s *ProjectService) DeleteProject(ctx context.Context, id primitive.ObjectID) error {
+ log.Printf("INFO: Deleting project with ID %s", id.Hex())
+
+ // The repository handles the cascading deletion of requirements
+ // This ensures that no orphaned requirements remain in the database
+ err := s.repo.Delete(ctx, id)
+ if err != nil {
+ log.Printf("ERROR: Failed to delete project %s: %v", id.Hex(), err)
+ return err
+ }
+
+ log.Printf("INFO: Project %s and all its requirements deleted successfully", id.Hex())
+ return nil
+}
+
+// SearchProjects provides flexible project search functionality
+// This method handles both empty queries (return all projects) and text-based searches
+func (s *ProjectService) SearchProjects(ctx context.Context, query string, userID uuid.UUID) ([]*Project, error) {
+ // Handle empty search queries by returning all projects for the user
+ // This provides a consistent interface regardless of whether a search term is provided
+ if query == "" {
+ log.Printf("INFO: Empty search query, returning all projects for user %s", userID)
+ return s.repo.FindByCreatedBy(ctx, userID)
+ }
+
+ log.Printf("INFO: Searching projects for user %s with query '%s'", userID, query)
+
+ // Delegate to repository for the actual search implementation
+ // The repository handles the specifics of text searching across project fields
+ projects, err := s.repo.Search(ctx, query, userID)
+ if err != nil {
+ log.Printf("ERROR: Search failed for query '%s' and user %s: %v", query, userID, err)
+ return nil, err
+ }
+
+ log.Printf("INFO: Search completed - found %d projects for query '%s'", len(projects), query)
+ return projects, nil
+}