Øvelse 26 – Fra DTO til model med domæneprimitiver
Source code
First name
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace ValidateTheNameModelBinding.Models
{
public record class Firstname
{
public string Value { get; }
public Firstname(string firstname)
{
// Validering
if (string.IsNullOrWhiteSpace(firstname) || firstname.Length < 2 || firstname.Length > 20)
throw new ArgumentException("Efternavn skal være mellem 2 og 20 tegn.");
Regex nameRegex = new Regex(@"^[A-Za-zÆØÅæøå]+$");
if (!nameRegex.IsMatch(firstname)) throw new ArgumentException($"Fornavn må kun bestå af store og små bogstaver mellem a og å.", nameof(firstname));
Value = firstname;
}
}
}
Last name
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace ValidateTheNameModelBinding.Models
{
public record class Lastname
{
public string Value { get; }
public Lastname(string lastname)
{
// Validering
if (string.IsNullOrWhiteSpace(lastname) || lastname.Length < 2 || lastname.Length > 20)
throw new ArgumentException("Efternavn skal være mellem 2 og 20 tegn.");
Regex nameRegex = new Regex(@"^[A-Za-zÆØÅæøå]+$");
if (!nameRegex.IsMatch(lastname)) throw new ArgumentException($"Efternavn må kun bestå af store og små bogstaver mellem a og å.", nameof(lastname));
Value = lastname;
}
}
}
Person
using System.Text.RegularExpressions;
using System.Xml.Linq;
namespace ValidateTheNameModelBinding.Models
{
public class Person
{
private Firstname firstName;
private Lastname lastName;
public Person(Firstname firstName, Lastname lastName)
{
if (firstName == null) throw new ArgumentNullException(nameof(firstName));
if (lastName == null) throw new ArgumentNullException(nameof(lastName));
this.firstName = firstName;
this.lastName = lastName;
}
}
}
Name Controller
using Microsoft.AspNetCore.Mvc;
using ValidateTheNameModelBinding.Models;
using ValidateTheNameModelBinding.Util;
namespace ValidateTheNameWebApplication.Controllers
{
public class NameController : Controller
{
[HttpGet]
public IActionResult Index()
{
return View("Index", new { nameIsValid = false, showNameEvaluation = false });
}
[HttpPost]
public IActionResult ValidateName(PersonDTO person)
{
if (!ModelState.IsValid)
{
return View("Index", new {nameIsValid = false, showNameEvaluation = true });
}
//DI intentionally omittede here for clarity
PersonRepository personRepository = new PersonRepository();
Person personWithInvariance = new Person(new Firstname(person.Firstname), new Lastname(person.Lastname));
personRepository.AddPerson(personWithInvariance);
return View("Index", new { nameIsValid = true, showNameEvaluation = true });
}
}
}
Reflekterende spørgsmål
- Hvilke fordele opnår man ved at anvende domæneprimitiver fremfor rå datatyper i modeller?
Man opnår muligheden for at implementere regler (ivarians) som de skal opfylde. Hvis de ikke opfyldes kan domæneprimitiven og dermed værdien ikke oprettes (fail fast).
- Hvordan understøtter brugen af domæneprimitiver principperne om sikker softwareudvikling og defense in depth?
Det støtter det ved at systemet i sig selv bliver mere sikekert da der altid valideres ved oprettelse samtidigt med at andre udviklere tvinges til at bruge dem korrekt (sikker softwareudvikling). Hvis det så er en API man arbejder på, jamen så sikrer validering her at selv hvis validering i frontend (anden side af tillidsgrænse) ikke virker, så har man et ekstra lag (defense in depth).
- Hvordan hjælper denne tilgang med at undgå, at ugyldige data forplanter sig gennem systemet?
Ved at det simpelthen ikke kan lade sig gøre, hvis man altså har opsat det korrekt og med de rigtige invarians regler / domæneregler.
- Hvilke overvejelser bør man gøre sig omkring performance og kompleksitet, når man bruger domæneprimitiver i større systemer?
Hvis det implementeres korrekt så bør performance ikke lide den store last, medmindre man arbejder med meget store mængder data. Kompleksiteten kan være større når man altid skal instanciere domæneprimitiverne (fx. til at oprette en person), men ved at få validering centreret i egne klasser kan fx. domæmemodellerne blive mere overskuelige uden chance for at validering glemmes.
- Hvordan kobler denne metode sammenhængen mellem DTO og domænemodellen i softwarearkitekturen?
Ved at konventere fra DTO i controlleren (præsentations laget) til domænemodelle (applikations-/forretnings laget) får man valideret de input som kommer ind helt automatisk. Man skal dog sikre at ingen gør eller kan gøre brug af DTO'er inde i applikationslaget.
Kobling til standarder
| Element i øvelsen | Relateret standard / princip | Kommentar |
|---|---|---|
| Inputvalidering | ISO/IEC 27002:2022, afsnit 8.26 (applikationssikkerhed) | Anbefaler validering og integritetstjek af input til en applikation. |
| OWASP ASVS 5.0.0, kontrol 16.5 (Error Handling) | Krav om korrekt håndtering af fejl(undtagelser) | |
| Secure by Design (Johnsson et al.) | Fremhæver princippet om at skabe robuste domænemodeller og sikre objekters tilstand gennem konstruktion. Herunder domæne primitiver. |