diff --git a/pom.xml b/pom.xml
index 17fd7ad..f2ff7f1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,10 +23,20 @@
org.springframework.boot
spring-boot-starter-actuator
+
+
+
+
org.springframework.boot
- spring-boot-starter-data-jdbc
+ spring-boot-starter-data-jpa
+
+ org.projectlombok
+ lombok
+ provided
+
+
org.springframework.boot
spring-boot-starter-web
diff --git a/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java b/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java
new file mode 100644
index 0000000..e2c792b
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java
@@ -0,0 +1,24 @@
+package com.zoomcare.candidatechallenge.controller;
+
+import com.zoomcare.candidatechallenge.model.EmployeeWithReports;
+import org.springframework.http.ResponseEntity;
+
+import java.util.List;
+
+public interface EmployeeController {
+
+ /**
+ * Returns a list of all top-level employees.
+ *
+ * @return the list of employees
+ */
+ ResponseEntity> getAll();
+
+ /**
+ * Returns the employee with the given ID or a 404 response if no such employee exists.
+ *
+ * @param id the employee ID
+ * @return the employee
+ */
+ ResponseEntity getById(Long id);
+}
diff --git a/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeControllerImpl.java b/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeControllerImpl.java
new file mode 100644
index 0000000..a1232a3
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeControllerImpl.java
@@ -0,0 +1,35 @@
+package com.zoomcare.candidatechallenge.controller;
+
+import com.zoomcare.candidatechallenge.model.EmployeeWithReports;
+import com.zoomcare.candidatechallenge.service.EmployeeService;
+import lombok.AllArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Optional;
+
+
+@RestController
+@RequestMapping("/employees")
+@AllArgsConstructor
+public class EmployeeControllerImpl implements EmployeeController {
+
+ private EmployeeService employeeService;
+
+ @GetMapping("/all")
+ public ResponseEntity> getAll() {
+ return ResponseEntity.ok(employeeService.getAll());
+ }
+
+ @GetMapping("/id/{id}")
+ public ResponseEntity getById(@PathVariable Long id) {
+ return Optional.ofNullable(employeeService.getById(id))
+ .map(ResponseEntity::ok) // if exists, return 200 with body
+ .orElse(ResponseEntity.notFound().build()); // otherwise return a 404
+ }
+}
+
diff --git a/src/main/java/com/zoomcare/candidatechallenge/model/Employee.java b/src/main/java/com/zoomcare/candidatechallenge/model/Employee.java
new file mode 100644
index 0000000..729a22e
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/model/Employee.java
@@ -0,0 +1,23 @@
+package com.zoomcare.candidatechallenge.model;
+
+import lombok.Data;
+
+import javax.persistence.*;
+import java.util.HashMap;
+import java.util.Map;
+
+@Entity
+@Data
+public class Employee {
+
+ @Id
+ public Long id;
+
+ public Long supervisorId;
+
+ @ElementCollection
+ @MapKeyColumn(name = "key")
+ @Column(name = "value")
+ @CollectionTable(name = "property", joinColumns = @JoinColumn(name = "employee_id"))
+ public Map properties = new HashMap<>();
+}
diff --git a/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeWithReports.java b/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeWithReports.java
new file mode 100644
index 0000000..66cdc50
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeWithReports.java
@@ -0,0 +1,18 @@
+package com.zoomcare.candidatechallenge.model;
+
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class EmployeeWithReports {
+
+ private Long id;
+
+ private Long supervisorId;
+
+ private Map properties;
+
+ private List reports;
+}
diff --git a/src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java b/src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java
new file mode 100644
index 0000000..a96ea75
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java
@@ -0,0 +1,13 @@
+package com.zoomcare.candidatechallenge.repository;
+
+import com.zoomcare.candidatechallenge.model.Employee;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface EmployeeRepository extends JpaRepository {
+
+ List findBySupervisorId(Long supervisorId);
+
+ List findBySupervisorIdIsNull();
+}
diff --git a/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java
new file mode 100644
index 0000000..3165b84
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java
@@ -0,0 +1,24 @@
+package com.zoomcare.candidatechallenge.service;
+
+import com.zoomcare.candidatechallenge.model.EmployeeWithReports;
+import org.springframework.web.bind.annotation.PathVariable;
+
+import java.util.List;
+
+public interface EmployeeService {
+
+ /**
+ * Returns all top-level employees.
+ *
+ * @return the list of employees
+ */
+ List getAll();
+
+ /**
+ * Returns the employee with the given ID, or null if no such employee exists.
+ *
+ * @param id the employee ID
+ * @return the employee
+ */
+ EmployeeWithReports getById(@PathVariable Long id);
+}
diff --git a/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeServiceImpl.java b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeServiceImpl.java
new file mode 100644
index 0000000..c907d68
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeServiceImpl.java
@@ -0,0 +1,65 @@
+package com.zoomcare.candidatechallenge.service;
+
+import com.zoomcare.candidatechallenge.model.Employee;
+import com.zoomcare.candidatechallenge.model.EmployeeWithReports;
+import com.zoomcare.candidatechallenge.repository.EmployeeRepository;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.web.bind.annotation.PathVariable;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@AllArgsConstructor
+public class EmployeeServiceImpl implements EmployeeService {
+
+ private EmployeeRepository employeeRepository;
+
+ public List getAll() {
+ return employeeRepository
+ .findBySupervisorIdIsNull()
+ .stream()
+ .map(this::getEmployeeWithReports)
+ .collect(Collectors.toList());
+ }
+
+ public EmployeeWithReports getById(@PathVariable Long id) {
+ if (!employeeRepository.existsById(id)) {
+ return null;
+ }
+ return employeeRepository
+ .findById(id)
+ .map(this::getEmployeeWithReports)
+ .orElse(null);
+ }
+
+ /**
+ * Creates a new EmployeeWithReports object.
+ *
+ * @param employee the DAU instance to copy.
+ * @return an EmployeeWithReports object.
+ */
+ private EmployeeWithReports getEmployeeWithReports(Employee employee) {
+ EmployeeWithReports result = new EmployeeWithReports();
+ result.setId(employee.getId());
+ result.setSupervisorId(employee.getSupervisorId());
+ result.setProperties(employee.getProperties());
+ result.setReports(collectReportsFor(employee.getId()));
+ return result;
+ }
+
+ /**
+ * Recursively collects reports via findBySupervisorId.
+ *
+ * @param id the id of the superior employee.
+ * @return a list of reports.
+ */
+ private List collectReportsFor(Long id) {
+ return employeeRepository
+ .findBySupervisorId(id)
+ .stream()
+ .map(this::getEmployeeWithReports)
+ .collect(Collectors.toList());
+ }
+}