- Domain layer: Lesson entity, GermanWord value object, repository interfaces - Application layer: CQRS commands, DTOs with mapping - Infrastructure layer: EF Core with SQLite, LessonRepository - Presentation layer: Minimal API endpoints for lessons CRUD Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
50 lines
1.8 KiB
C#
50 lines
1.8 KiB
C#
using GermanApp.Domain.Exceptions;
|
|
|
|
namespace GermanApp.Domain.ValueObjects;
|
|
|
|
/// <summary>
|
|
/// Represents a German word with its translation and metadata.
|
|
/// This is a value object - it has no identity and is defined by its attributes.
|
|
/// </summary>
|
|
public record GermanWord
|
|
{
|
|
public string Value { get; }
|
|
public string Translation { get; }
|
|
public string? Article { get; } // der, die, das, or null for verbs/adjectives
|
|
public string PartOfSpeech { get; }
|
|
|
|
public GermanWord(string value, string translation, string? article, string partOfSpeech)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
throw new DomainException("German word cannot be empty");
|
|
|
|
if (string.IsNullOrWhiteSpace(translation))
|
|
throw new DomainException("Translation cannot be empty");
|
|
|
|
if (string.IsNullOrWhiteSpace(partOfSpeech))
|
|
throw new DomainException("Part of speech is required");
|
|
|
|
if (article != null && !IsValidArticle(article))
|
|
throw new DomainException("Invalid article. Must be 'der', 'die', or 'das'");
|
|
|
|
Value = value.Trim();
|
|
Translation = translation.Trim();
|
|
Article = article?.Trim();
|
|
PartOfSpeech = partOfSpeech.Trim();
|
|
}
|
|
|
|
private static bool IsValidArticle(string article) =>
|
|
article.Equals("der", StringComparison.OrdinalIgnoreCase) ||
|
|
article.Equals("die", StringComparison.OrdinalIgnoreCase) ||
|
|
article.Equals("das", StringComparison.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// Check if this word has a definite article.
|
|
/// </summary>
|
|
public bool HasArticle() => Article != null;
|
|
|
|
/// <summary>
|
|
/// Get the word with its article (if applicable).
|
|
/// </summary>
|
|
public string GetWithArticle() => Article != null ? $"{Article} {Value}" : Value;
|
|
}
|