# Kickoff Java Project — Redis/Cache Module

Redis caching module (conditional: Redis=Yes).

Reference: Janus `janus-webapp/src/main/java/.../config/cache/` and `janus-core/src/main/java/.../config/cache/`.

---

## Dependencies

**webapp POM:**

```xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency>
```

**core POM:**

```xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency>
```

---

## CacheConfiguration

Located in webapp: `src/main/java/<base-package>/webapp/config/cache/CacheConfiguration.java`

```java
@Configuration
@EnableCaching
@EnableConfigurationProperties(CacheConfigurationProperties.class)
@ConditionalOnProperty(name = "humand.<project>.cache.enabled", havingValue = "true", matchIfMissing = true)
public class CacheConfiguration {

    @Bean
    public RedisCacheManager cacheManager(
            RedisConnectionFactory connectionFactory, CacheConfigurationProperties cacheProperties) {
        Duration ttl = Duration.ofMinutes(cacheProperties.ttlMinutes());
        RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(ttl)
                .serializeKeysWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
                        new GenericJackson2JsonRedisSerializer()))
                .disableCachingNullValues();

        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(cacheConfig)
                .build();
    }
}
```

---

## CacheConfigurationProperties

Located in webapp: `src/main/java/<base-package>/webapp/config/cache/CacheConfigurationProperties.java`

```java
@ConfigurationProperties("humand.<project>.cache")
public record CacheConfigurationProperties(boolean enabled, int ttlMinutes) {}
```

---

## CacheNames

Located in core: `src/main/java/<base-package>/core/config/cache/CacheNames.java`

```java
public final class CacheNames {

    private CacheNames() {}

    public static final String SAMPLE_CACHE = "<PROJECT>:v1:SAMPLE_CACHE";
}
```

---

## Service usage

```java
@Cacheable(value = CacheNames.SAMPLE_CACHE, key = "#id")
public SampleEntity findById(long id) { ... }

@CacheEvict(value = CacheNames.SAMPLE_CACHE, allEntries = true)
@Transactional
public void update(SampleEntity entity) { ... }
```

---

## application.yml

```yaml
humand:
  <project>:
    cache:
      enabled: true
      ttl-minutes: 15

spring:
  data:
    redis:
      host: ${SPRING_DATA_REDIS_HOST:localhost}
      port: ${SPRING_DATA_REDIS_PORT:6379}
      database: 0
      timeout: 2s
      connect-timeout: 2s
```

---

## application-test.yml

Disable Redis in tests so integration tests run without a Redis instance:

```yaml
humand:
  <project>:
    cache:
      enabled: false

spring:
  autoconfigure:
    exclude:
      - org.springframework.boot.data.redis.autoconfigure.DataRedisAutoConfiguration
      - org.springframework.boot.data.redis.autoconfigure.DataRedisRepositoriesAutoConfiguration
```

Note the SB4 package path: `org.springframework.boot.data.redis.autoconfigure` (not `org.springframework.boot.autoconfigure.data.redis`).

---

## Infrastructure (Terraform)

SSM parameters (read from `/common/cache/`):

| SSM Path | Env Var |
|----------|---------|
| `/common/cache/hostname` | `SPRING_DATA_REDIS_HOST` |
| `/common/cache/port` | `SPRING_DATA_REDIS_PORT` |
| `/common/cache/password` | (secret, via SSM) |

Additional env vars in project module:

```hcl
{ name = "SPRING_DATA_REDIS_DATABASE", value = "0" }
{ name = "SPRING_DATA_REDIS_SSL_ENABLED", value = "true" }
```
