5.8 KiB
5.8 KiB
Condado Newsletter Bot
A newsletter bot built with Kotlin and Spring Boot. This file gives Claude persistent instructions and context about the project so every session starts with the right knowledge.
Project Overview
- Language: Kotlin (JVM)
- Framework: Spring Boot 3.x
- Purpose: Automate the creation, management, and delivery of newsletters
- Architecture: REST API backend with scheduled jobs for sending newsletters
Tech Stack
| Layer | Technology |
|---|---|
| Language | Kotlin |
| Framework | Spring Boot 3.x |
| Build Tool | Gradle (Kotlin DSL - build.gradle.kts) |
| Database | PostgreSQL (via Spring Data JPA) |
| Spring Mail (SMTP / JavaMailSender) | |
| Scheduler | Spring @Scheduled tasks |
| Testing | JUnit 5 + MockK |
| Docs | Springdoc OpenAPI (Swagger UI) |
Project Structure
src/
├── main/
│ ├── kotlin/com/condado/newsletter/
│ │ ├── CondadoNewsletterApplication.kt # App entry point
│ │ ├── config/ # Spring configuration classes
│ │ ├── controller/ # REST controllers
│ │ ├── service/ # Business logic
│ │ ├── repository/ # Spring Data JPA repositories
│ │ ├── model/ # JPA entities
│ │ ├── dto/ # Data Transfer Objects
│ │ └── scheduler/ # Scheduled tasks
│ └── resources/
│ ├── application.yml # Main config
│ ├── application-dev.yml # Dev profile config
│ └── templates/ # Email HTML templates (Thymeleaf)
└── test/
└── kotlin/com/condado/newsletter/ # Tests mirror main structure
Build & Run Commands
# Build the project
./gradlew build
# Run the application (dev profile)
./gradlew bootRun --args='--spring.profiles.active=dev'
# Run all tests
./gradlew test
# Run a specific test class
./gradlew test --tests "com.condado.newsletter.service.NewsletterServiceTest"
# Generate OpenAPI docs (served at /swagger-ui.html when running)
./gradlew bootRun
Coding Standards
- Use Kotlin idiomatic style: data classes, extension functions, and null-safety operators.
- Prefer
valovervarwherever possible. - Use constructor injection for dependencies (never field injection with
@Autowired). - All DTOs must be data classes with validation annotations (
javax.validation). - Controller methods must return
ResponseEntity<T>with explicit HTTP status codes. - Services must be annotated with
@Serviceand never depend on controllers. - Repositories must extend
JpaRepository<Entity, IdType>. - Use
@Transactionalon service methods that modify data. - All public functions must have KDoc comments.
- Use
snake_casefor database columns andcamelCasefor Kotlin properties. - Keep controllers thin — business logic belongs in services.
Naming Conventions
| Artifact | Convention | Example |
|---|---|---|
| Classes | PascalCase | NewsletterService |
| Functions | camelCase | sendNewsletter() |
| Variables | camelCase | subscriberList |
| Constants | SCREAMING_SNAKE_CASE | MAX_RETRIES |
| DB tables | snake_case (plural) | newsletter_subscribers |
| REST endpoints | kebab-case | /api/v1/newsletter-issues |
| Packages | lowercase | com.condado.newsletter |
Testing Guidelines
- Every service class must have a corresponding unit test class.
- Use MockK for mocking (not Mockito).
- Integration tests use
@SpringBootTestand an H2 in-memory database. - Test method names follow the pattern:
should_[expectedBehavior]_when_[condition]. - Minimum 80% code coverage for service classes.
Environment Variables
| Variable | Description |
|---|---|
SPRING_DATASOURCE_URL |
PostgreSQL connection URL |
SPRING_DATASOURCE_USERNAME |
DB username |
SPRING_DATASOURCE_PASSWORD |
DB password |
MAIL_HOST |
SMTP host |
MAIL_PORT |
SMTP port |
MAIL_USERNAME |
SMTP username |
MAIL_PASSWORD |
SMTP password |
⚠️ Never hardcode credentials. Always use environment variables or a
.envfile (gitignored).
Key Domain Concepts
- Subscriber: A person who opted in to receive newsletters.
- NewsletterIssue: A single newsletter edition with a subject and HTML body.
- Campaign: A scheduled or triggered dispatch of a
NewsletterIssueto a group of subscribers. - SendLog: A record of each email send attempt (status: PENDING / SENT / FAILED).
Git Workflow
- Branch naming:
feature/<short-description>,fix/<short-description>,chore/<short-description> - Commit messages follow Conventional Commits:
feat:,fix:,chore:,docs:,test: - PRs require at least one passing CI check before merging.
- Never commit directly to
main.