Systeme d'alarme IoT evenementiel : hexagonal, CQRS, Kafka
Backend d'un systeme de video-surveillance dont le capteur est un iPhone recycle. Le probleme est simple, la structure est le sujet : - domain/ sans aucun import de framework (verifie par 7 regles ArchUnit qui tournent aussi pendant le docker build) - CQRS strict : write et read ne partagent ni modele ni port - AlarmDecision en sealed interface -> switch exhaustif verifie a la compilation, plutot qu'une chaine de if - Kafka en KRaft (Zookeeper est supprime depuis Kafka 4.0), cle de partition = deviceId pour garantir l'ordre par capteur - ErrorHandlingDeserializer + DLQ : un message malforme ne bloque pas le flux des evenements suivants - notification WebSocket differee apres commit, pour ne jamais afficher une intrusion qui partirait en rollback 31 tests au vert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
commit
63ead8a966
81 changed files with 5051 additions and 0 deletions
|
|
@ -0,0 +1,53 @@
|
|||
package me.belkacemi.iotalarm.domain.model;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Value Object identifiant un capteur physique.
|
||||
*
|
||||
* <p>Dans le studio il n'y en a qu'un : {@code iphone-biblio}, un vieil iPhone
|
||||
* recyclé, calé entre deux livres de la bibliothèque.</p>
|
||||
*
|
||||
* <p>L'identifiant est contraint dès la construction. Le domaine refuse les
|
||||
* données invalides à sa frontière, il n'a donc jamais à s'en méfier ensuite.</p>
|
||||
*/
|
||||
public record DeviceId(String value) {
|
||||
|
||||
private static final Pattern VALID = Pattern.compile("^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$");
|
||||
|
||||
public DeviceId {
|
||||
if (value == null || value.isBlank()) {
|
||||
throw new IllegalArgumentException("L'identifiant de capteur est obligatoire");
|
||||
}
|
||||
value = value.trim().toLowerCase();
|
||||
if (!VALID.matcher(value).matches()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Identifiant de capteur invalide : '" + value
|
||||
+ "' (minuscules, chiffres et tirets, 3 a 64 caracteres)");
|
||||
}
|
||||
}
|
||||
|
||||
public static DeviceId of(String value) {
|
||||
return new DeviceId(value);
|
||||
}
|
||||
|
||||
/** Libelle lisible affiche dans la console : {@code iphone-biblio} -> {@code "Iphone Biblio"}. */
|
||||
public String displayName() {
|
||||
StringBuilder sb = new StringBuilder(value.length());
|
||||
for (String part : value.split("-")) {
|
||||
if (part.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (!sb.isEmpty()) {
|
||||
sb.append(' ');
|
||||
}
|
||||
sb.append(Character.toUpperCase(part.charAt(0))).append(part.substring(1));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue