Enterprise event management platform development guidelines for Spring Boot 3 + Kotlin backend with React + TypeScript frontend. Covers architecture patterns, service layer design, testing strategies, and infrastructure setup.
EventR is a full-stack enterprise event management platform built with **Spring Boot 3 + Kotlin** backend and **React + TypeScript** frontend. The system implements SOLID principles, layered architecture, and event-driven patterns for corporate event lifecycle management (creation → registration → check-in → analytics).
```
com.eventr/
├── controller/ # 19 REST controllers (@RestController)
├── service/ # 20 business services (some need interface extraction)
├── repository/ # 14 JPA repositories (extends JpaRepository)
├── model/ # 14 JPA entities (@Entity with relationships)
├── dto/ # 16 data transfer objects (request/response)
├── config/ # Spring configuration (@Configuration)
└── events/ # Domain events & event handlers
```
```
src/
├── components/ # Reusable UI components
├── pages/ # Route-level components
├── api/ # API client functions
├── hooks/ # Custom React hooks
└── utils/ # Helper functions
```
When creating or modifying services, always follow the interface-implementation pattern:
```kotlin
// Define interface
interface EventService {
fun createEvent(dto: CreateEventDto): EventDto
fun getEventById(id: UUID): EventDto
}
// Implement service
@Service
class EventServiceImpl(
private val eventRepository: EventRepository,
private val applicationEventPublisher: ApplicationEventPublisher
) : EventService {
override fun createEvent(dto: CreateEventDto): EventDto {
val event = Event(/* map from dto */)
val savedEvent = eventRepository.save(event)
applicationEventPublisher.publishEvent(EventCreatedEvent(savedEvent))
return savedEvent.toDto()
}
override fun getEventById(id: UUID): EventDto {
return eventRepository.findById(id)
.orElseThrow { EntityNotFoundException("Event not found") }
.toDto()
}
}
```
**Anti-patterns to avoid:**
Use JPA repositories with custom query methods:
```kotlin
interface EventRepository : JpaRepository<Event, UUID> {
fun findByTypeAndStatus(type: EventType, status: EventStatus): List<Event>
@Query("SELECT e FROM Event e WHERE e.startDate BETWEEN :start AND :end")
fun findEventsInDateRange(start: LocalDateTime, end: LocalDateTime): List<Event>
}
```
Publish domain events for cross-cutting concerns (webhooks, emails, analytics):
```kotlin
// In service methods
applicationEventPublisher.publishEvent(UserRegisteredEvent(registration))
applicationEventPublisher.publishEvent(UserCheckedInEvent(checkIn))
// Event listener
@Component
class EventEventListener(
private val webhookDeliveryService: WebhookDeliveryService
) {
@EventListener
fun handleUserRegistered(event: UserRegisteredEvent) {
webhookDeliveryService.deliverWebhooksForEvent("USER_REGISTERED", event.payload)
}
}
```
Always write tests using Spring Boot test configuration:
```kotlin
@SpringBootTest
@ActiveProfiles("test")
@Import(TestConfig::class)
class EventServiceTest {
@Autowired
private lateinit var eventService: EventService
@Autowired
private lateinit var eventRepository: EventRepository
@Test
fun `should create event successfully`() {
val dto = CreateEventDto(/* test data */)
val result = eventService.createEvent(dto)
assertNotNull(result.id)
assertEquals(dto.name, result.name)
}
}
```
**Testing environment:**
Create typed API client functions in `src/api/`:
```typescript
// src/api/eventAPI.ts
export const eventAPI = {
createEvent: (data: CreateEventDto): Promise<EventDto> =>
apiClient.post('/api/events', data),
getEvents: (): Promise<EventDto[]> =>
apiClient.get('/api/events'),
getEventById: (id: string): Promise<EventDto> =>
apiClient.get(`/api/events/${id}`)
};
```
**Start full development environment:**
```bash
./start-dev.sh # Recommended - starts all services
```
**Or start services individually:**
```bash
docker-compose up -d # PostgreSQL, Redis, LocalStack, MailHog
./mvnw spring-boot:run -Pbackend
cd frontend && npm start
```
**Run tests:**
```bash
./mvnw test
cd frontend && npm test
```
Use UUID primary keys and JPA relationships:
```kotlin
@Entity
@Table(name = "events")
class Event(
@Id
val id: UUID = UUID.randomUUID(),
@Column(nullable = false)
var name: String,
@OneToMany(mappedBy = "event", cascade = [CascadeType.ALL])
val sessions: List<Session> = mutableListOf(),
@OneToMany(mappedBy = "event")
val registrations: List<Registration> = mutableListOf(),
@Column(name = "created_at", nullable = false, updatable = false)
val createdAt: LocalDateTime = LocalDateTime.now(),
@Column(name = "updated_at")
var updatedAt: LocalDateTime = LocalDateTime.now()
)
```
**Core domain model:**
```
Event (1) → (many) Registration → (many) CheckIn
Event (1) → (many) Session → (many) CheckIn
Resource (many) ← → (many) Session (booking system)
```
Webhooks automatically fire on domain events with retry logic. Use `webhook-client/` (port 3002) to test locally.
1. **Always create service interfaces** before implementation
2. **Publish domain events** for cross-cutting concerns
3. **Use UUID primary keys** for all entities
4. **Write integration tests** with `@SpringBootTest` and `@ActiveProfiles("test")`
5. **Follow layered architecture**: Controller → Service → Repository
6. **Keep services focused** - split large services (>600 lines) into smaller domain services
7. **Use TypeScript types** for all API interactions in frontend
8. **Ensure Docker infrastructure is running** before starting backend
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/github-copilot-instructions-for-eventr/raw