JobSteer is a web application designed to facilitate job matching between job seekers and recruiters. The platform leverages modern technologies and software engineering principles to provide two main functionalities:
- For Job Seekers: Upload resumes and receive matching job recommendations
- For Recruiters: Post job opportunities and receive matching candidate recommendations
The backend follows a layered architecture with clear separation of concerns:
Controllers Layer:
- AuthController: Handles authentication and user management
- ResumeController: Manages resume uploads and processing
- JobPostController: Handles job posting operations
- MatchingController: Manages the matching functionality
Service Layer:
- ResumeService: Resume processing and management
- JobPostService: Job posting management
- MatchingService: Implements matching algorithms
- UserService: User management operations
Repository Layer:
- Uses Spring Data JPA for database operations
- Clear interfaces for each entity type
The frontend uses Vue.js with a well-organized structure:
Views:
- HomeView
- UploadResumeView
- RecruiterDashboardView
- JobDetailView
State Management:
- Uses Pinia for state management
- Separate stores for users, resumes, and job posts
Single Responsibility Principle Example:
public class JobPostService {
@Autowired
private JobPostRepository jobPostRepository;
public JobPost createJobPost(JobPostRequest request) {
// Job post creation logic
}
}Open/Closed Principle Example:
public abstract class User {
private Long id;
private String email;
private String password;
}
public class JobSeeker extends User {
private Resume resume;
private String location;
}Factory Pattern Example:
@Component
public class UserFactory {
public User createUser(RegistrationRequest request, String encodedPassword) {
switch (request.getUserType().toLowerCase()) {
case "jobseeker":
return createJobSeeker(request, encodedPassword);
case "recruiter":
return createRecruiter(request, encodedPassword);
default:
throw new IllegalArgumentException("Unknown user type");
}
}
}Repository Pattern Example:
@Repository
public interface JobPostRepository extends JpaRepository<JobPost, Integer> {
List<JobPost> findByRecruiterId(Long recruiterId);
void deleteBySource(String source);
}Our testing implementation follows a systematic approach with three key aspects:
- Controller layer tests for API endpoints
- Service layer tests for business logic
- Repository layer integration tests
- Security testing across all layers
- Proper Mocking and Dependency Injection
@MockBean
private JobPostService jobPostService;
@MockBean
private AuthenticationManager authenticationManager;- Security Context Testing
@WithMockUser(roles = "JOBSEEKER")
public void uploadResume_Success() {
// Test with security context
}- Comprehensive Error Handling
@Test
public void loginUser_InvalidCredentials() throws Exception {
mockMvc.perform(post("/api/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginRequest)))
.andExpect(status().isUnauthorized());
}- Authentication System
- Login success/failure scenarios
- JWT token validation
- Role-based access control
- Registration validation
- Job Post Management
- CRUD operations validation
- Permission checks
- Data validation
- Resource existence verification
- Resume Management
- File upload validation
- Access control checks
- Data persistence verification
- Error handling scenarios
Each test class follows a clear pattern:
- Setup of mock dependencies
- Definition of test data
- Execution of the test scenario
- Verification of results and side effects
Example of this pattern:
@Test
@WithMockUser(roles = "JOBSEEKER")
public void uploadResume_Success() throws Exception {
// 1. Setup
MockMultipartFile file = new MockMultipartFile(
"file", "resume.pdf",
MediaType.APPLICATION_PDF_VALUE,
"PDF content".getBytes()
);
// 2. Test Data
Map<String, Object> response = new HashMap<>();
response.put("status", "success");
response.put("resumeId", 1);
// 3. Mock Setup
when(resumeService.uploadResume(anyLong(), any()))
.thenReturn(ResponseEntity.ok(response));
// 4. Execution & Verification
mockMvc.perform(multipart("/api/resume/upload/{jobSeekerId}", jobSeekerId)
.file(file))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("success"));
}Our testing infrastructure includes:
- MockMvc for API testing
- JUnit 5 as the testing framework
- Mockito for mocking dependencies
- Spring Security Test for security context
- Custom test configurations for different scenarios
@Component
public class JwtFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) {
// JWT validation implementation
}
}@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) {
http.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}@Entity
public class Resume implements Matchable {
@OneToMany(mappedBy = "resume", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Experience> experiences;
@OneToMany(mappedBy = "resume", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Language> languages;
@OneToOne(cascade = CascadeType.ALL)
private JobSeeker jobSeeker;
}The JobSteer project demonstrates a robust implementation of software engineering principles through:
- Clear separation of concerns
- Proper use of design patterns
- Secure authentication and authorization
- Clean and maintainable code structure
- Comprehensive testing coverage
- Proper error handling and validation
The combination of well-structured code and thorough testing ensures the reliability and maintainability of the application while providing a solid foundation for future enhancements.