Immutable append only entity

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Jan Mikeš
Člen | 771
+
0
-

Ahoj, v souladu s doctrine best practices bych chtěl začít používat immutable entity, ale nejsem si jist, jestli můj návrh je „správně“ a proto bych rád zjistil co používají ti z vás, kteří s touto problematikou mají více zkušeností (bohužel google mě v tomto trošku zklamal a našel jsem jen omezené množství informací).

Cíl je mít všechny mé entity immutable, výhody i nevýhody jsou jasné, pro mě tedy nejdůležitější historie změn a cachování

Mám 2 způsoby jak řešit

  1. parent entita, která bude mít statické properties a kolekci dalších immutable entit, které v sobě ponesou ony „dynamické“ údaje
  2. Něco jako 2× property s id

Názorně (hodně zjednodušeně):

// 1)
class Article
{
	/** @var \DateTime */
	private $createdAt;

	/** @var ArticleHistory[] */
	private $historyChanges;


	public function __construct($text)
	{
		$this->createdAt = new \DateTime;
		$this->updateText($text);
	}


	public function updateText($text)
	{
		$this->historyChanges[] = new ArticleHistory($this, $text);
	}


	public function text()
	{
		// případně jiná logika, na vytáhnutí poslední změny
		return $this->historyChanges->last()->text();
	}
}

class ArticleHistory
{
	/** @var Article */
	private $article;

	/** @var \DateTime */
	private $createdAt;

	/** @var string */
	private $text;


	public function __construct(Article $article, $text)
	{
		$this->createdAt = new \DateTime;
		$this->article = $article;
		$this->text = $text;
	}


	public function text()
	{
		return $this->text;
	}
}
// 2)
class Article
{
	/** @var UuidInterface */
	private $id; // primary key

	/** @var UuidInterface */
	private $articleId; // non-unique

	/** @var string */
	private $text;


	public function __construct($text, $articleId = NULL)
	{
		$this->id = Uuid::uuid4();
		$this->articleId = $articleId ? $articleId : Uuid::uuid4();
		$this->text = $text;
	}


	public function updateText($text)
	{
		return new Article($text, $this->articleId);
	}


	public function text()
	{
		return $this->text;
	}
}

U obou přístupů mě napadnou výhody i nevýhody, věřím ale, že má mysl nedokáže pokrýt všechny situace, které mohou nastat, proto bych chtěl touto cestou poprosit ty z vás, kteří máte už praxi s immutable entitami o rady a příběhy ze života :-).

Děkuji!

edit: když se na to teď zpětně dívám, vesměs 1 a 2 je skoro to stejné jen s tím rozdílem, ze řešení č. 2 v sobě nese všechny informace a proto může být zjednodušeno do jedné entity

Editoval Jan Mikeš (28. 11. 2016 21:16)

newPOPE
Člen | 648
+
0
-

Ako sam pises ma to svoje pre a proti. Tiez som sa tymto pristupom/otazkou zaoberal.

V pripade 1 si v podstate mozes cache lahko pridat napr:

	public function updateText($text)
    {
		$this->text = $text;
        $this->historyChanges[] = new ArticleHistory($this, $text);
    }

V pripade 2 si skus predstavit co sa musi stat ked chces upravit dany text na entite?. Dostanes novu entitu Article co je super. No otazka je napr. ako upravit vsetky referencie zo starej na novu? :)

Cize tu by som sa skor siel cestou projektorov (v DDD sa to myslim tak vola). Tzn. ked upravim Article tak vyleti event, ze bol upraveny a kazdy kto potrebuje sa podla toho zachova.

  • History (si to moze ulozit)
  • Cache (si pregeneruje zaznam)
  • Search (takisto pregeneruje zdroj pre agregacie)

Dokonca ma dnes po ceste do prace napadlo API as a database. Kde si proste povies, ze slave databazy (read only) bude na citanie. Napr. frontend, vyhladavanie, 3rd party …

Svaťa Šimara
Člen | 98
+
0
-

Jestli rozumím správně – chceš mít v systému neměnné objekty a historii změn, ano z nějakých důvodů toho chceš dosáhnout, ok. Můžu vědět z jakých? Pro zajímavost.

Případ číslo 1 – spíš bych tuto entitu pojmenoval podle vzoru best practices „append only“, je totiž změnitelná – obsahuje měnitelnou kolekci. To, že ji můžu měnit, zůstává mi problém se souběhem zápisu do jedné entity. Že?

Článek má 2 položky historie
Proces 1 vytáhne entitu z DB
Proces 2 vytáhne entitu z DB
Proces 1 přidá historii a zapíše do DB 3 položky
Proces 2 přidá historii a zapíše do DB 3 položky – přepíše to, co udělal proces 1 :-(

Ok, tady je asi důležité, jak je implemetovaná persistence objektu, pokud ale jenom vím, že persistence zapisuje, pak musím počítat, že může přepisovat položky historie :-/

Pak jenom dotaz – proč musí historie znát svůj článek? Je to její zodpovědnost?

Případ 2 – ano, toto je immutable entita. Ale, ale… není to tak úplně entita. Entita je identifikovaná svojí identitou. V případě, že budu mít článek, a změním ho, vytvořím vlastně druhý článek, který bude mít ale stejnou identitu jako článek první. A jeje – mám v systému 2 objekty, které mají stejnou identitu, ale nejsou identické :-(

Než vymýšlet kola, podíval bych se po Event sourcing – ne, nemám s ním zkušenosti. A co jsem četl „člověk musí být fakt dobrý a vědět, co dělá, pokud má implementovat event sourcing“.