AutoFac IoC, DDD, and inter-Repository Dependencies - domain-driven-design

I have two POCO types, A and B. I have a repository for each, Rep<A> and Rep<B>, both of which implement IRep<A> and IRep<B> served up by an IoC container (AutoFac in this case).
There are several kinds of repositories - load-on-demand from a DB (or whatever), lazy-loaded in-memory collections, cached web-service results, etc. Callers can't tell the difference. Both Rep<A> and Rep<B> happen to be in-memory collections as A's and B's don't change very much and live a long time.
One of the properties of B is an A. What I do now is, when a B is asked for its A, B gets IRep<A> to find its A and returns it. It does this every time - every request for B's A involves IRep<A>.Find(). The upside is B's never hold onto A's and each request takes into account whatever the state of Rep happens to be. The downside is a lot of IoC/IRep<A> churn.
I am thinking of using the Lazy<T> pattern here so that a B asks IRep<A> once and holds onto what it got. But what happens if an A is deleted from its repository?
I am looking for a clean way for Rep<A> to notify whoever is interested when it has changed. In my example, a certain B's A may be deleted, so I would like Rep<A> to raise an event when something is deleted, or added, etc. Rep<B> might subscribe to this event to clean up any B's that refer to A's that are now gone, etc. How to wire it up?
Ideally nothing changes when instantiating a Rep<A>. It should have no idea who listens, and A's might be manipulated all day long without ever firing up a Rep.
But when Rep<B> is born it needs a way to subscribe to Rep<A>'s event. There might not be a Rep<A> alive yet, but surely there will be once a B is asked for its A, so it seems ok to fire up a Rep<A>.
In essense, when Rep<B> is instantiated, it want it to register itself with Rep<A> for the event notification. I don't want to pollute the IRep<T> interface becaue this shouldn't matter to anyone outside the Repository layer. And other types of repositories might not have to worry about this at all.
Does this make any sense?

What if you made the Rep<A> return an "observable" object that can evaluate to an A, and also has a subscribable event that is raised when something about that A changes? Just a thought. This way, you don't have to have the handlers check to make sure that their A changed; if the event they're listening for is fired, it concerns their instance and not any other.
You might code it as follows:
public class Observable<T>:IDisposable
{
private T instance;
public T Instance
{
get{return instance;}
set{
instance = value;
var handlers = ReferenceChanged;
if(handlers != null) handlers(this, instance);
}
public static implicit operator T(Observable<T> obs)
{
return obs.Instance;
}
//DO NOT attach anonymous delegates or lambdas to this event, or you'll cause a leak
public event EventHandler<T> ReferenceChanged;
public void Dispose()
{
var handlers = ReferenceChanged;
if(handlers != null) handlers(this, null);
foreach(var handler in handlers) ReferenceChanged -= handler;
}
}
public class Rep<T>
{
private Dictionary<T, Observable<T>> observableDictionary = new Dictionary<T, Observable<T>>();
...
public Observable<T> GetObservableFactory(Predicate<T> criteria)
{
//criteria should test only uniquely-identifying information
if(observableDictionary.Keys.Any(criteria))
return observableDictionary[observableDictionary.Keys.First(criteria)];
else
{
//TODO: get object from source according to criteria and set to variable queryResult
var observable = new Observable<T>{Instance = queryResult};
observableDictionary.Add(queryResult, observable);
return observable;
}
}
}
...
var observableA = myRepA.GetObservable(myCriteria);
observableA.ReferenceChanged += DoSomethingWhenReferenceChanges;
Now, the consuming code will be notified if the internal reference is changed, or the observable is disposed (which also disposes of the internal reference). To have the observable also notify consuming code if child references of As change, A must itself be observable, firing an event handled by Observable<T> which will "bubble" it through either ReferenceChanged or a more specific handler such as InstanceDataChanged,(or whatever you want to call it).

Related

What type of data should be passed to domain events?

I've been struggling with this for a few days now, and I'm still not clear on the correct approach. I've seen many examples online, but each one does it differently. The options I see are:
Pass only primitive values
Pass the complete model
Pass new instances of value objects that refer to changes in the domain/model
Create a specific DTO/object for each event with the data.
This is what I am currently doing, but it doesn't convince me. The example is in PHP, but I think it's perfectly understandable.
MyModel.php
class MyModel {
//...
private MediaId $id;
private Thumbnails $thumbnails;
private File $file;
//...
public function delete(): void
{
$this->record(
new MediaDeleted(
$this->id->asString(),
[
'name' => $this->file->name(),
'thumbnails' => $this->thumbnails->toArray(),
]
)
);
}
}
MediaDeleted.php
final class MediaDeleted extends AbstractDomainEvent
{
public function name(): string
{
return $this->payload()['name'];
}
/**
* #return array<ThumbnailArray>
*/
public function thumbnails(): array
{
return $this->payload()['thumbnails'];
}
}
As you can see, I am passing the ID as a string, the filename as a string, and an array of the Thumbnail value object's properties to the MediaDeleted event.
How do you see it? What type of data is preferable to pass to domain events?
Updated
The answer of #pgorecki has convinced me, so I will put an example to confirm if this way is correct, in order not to change too much.
It would now look like this.
public function delete(): void
{
$this->record(
new MediaDeleted(
$this->id,
new MediaDeletedEventPayload($this->file->copy(), $this->thumbnail->copy())
)
);
}
I'll explain a bit:
The ID of the aggregate is still outside the DTO, because MediaDeleted extends an abstract class that needs the ID parameter, so now the only thing I'm changing is the $payload array for the MediaDeletedEventPayload DTO, to this DTO I'm passing a copy of the value objects related to the change in the domain, in this way I'm passing objects in a reliable way and not having strange behaviours if I pass the same instance.
What do you think about it?
A domain event is simply a data-holding structure or class (DTO), with all the information related to what just happened in the domain, and no logic. So I'd say Create a specific DTO/object for each event with the data. is the best choice. Why don't you start with the less is more approach? - think about the consumers of the event, and what data might they need.
Also, being able to serialize and deserialize the event objects is a good practice, since you could want to send them via a message broker (although this relates more to integration events than domain events).

Coordinating emission and subscription in Kotlin coroutines with hot flows

I am trying to design an observable task-like entity which would have the following properties:
Reports its current state changes reactively
Shares state and result events: new subscribers will also be notified if the change happens after they've subscribed
Has a lifecycle (backed by CoroutineScope)
Doesn't have suspend functions in the interface (because it has a lifecycle)
The very basic code is something like this:
class Worker {
enum class State { Running, Idle }
private val state = MutableStateFlow(State.Idle)
private val results = MutableSharedFlow<String>()
private val scope = CoroutineScope(Dispatchers.Default)
private suspend fun doWork(): String {
println("doing work")
return "Result of the work"
}
fun start() {
scope.launch {
state.value = State.Running
results.emit(doWork())
state.value = State.Idle
}
}
fun state(): Flow<State> = state
fun results(): Flow<String> = results
}
The problems with this arise when I want to "start the work after I'm subscribed". There's no clear way to do that. The simplest thing doesn't work (understandably):
fun main() {
runBlocking {
val worker = Worker()
// subscriber 1
launch {
worker.results().collect { println("received result $it") }
}
worker.start()
// subscriber 2 can also be created "later" and watch
// for state()/result() changes
}
}
This prints only "doing work" and never prints a result. I understand why this happens (because collect and start are in separate coroutines, not synchronized in any way).
Adding a delay(300) to coroutine inside doWork "fixes" things, results are printed, but I'd like this to work without artificial delays.
Another "solution" is to create a SharedFlow from results() and use its onSubscription to call start(), but that didn't work either last time I've tried.
My questions are:
Can this be turned into something that works or is this design initially flawed?
If it is flawed, can I take some other approach which would still hit all the goals I have specified in the beginning of the post?
Your problem is that your SharedFlow has no buffer set up, so it is emitting results to its (initially zero) current collectors and immediately forgetting them. The MutableSharedFlow() function has a replay parameter you can use to determine how many previous results it should store and replay to new collectors. You will need to decide what replay amount to use based on your use case for this class. For simply displaying latest results in a UI, a common choice is a replay of 1.
Depending on your use case, you may want to give your CoroutineScope a SupervisorJob() in its context so it isn't destroyed by any child job failing.
Side note, your state() and results() functions should be properties by Kotlin convention, since they do nothing but return references. Personally, I would also have them return read-only StateFlow/SharedFlow instead of just Flow to clarify that they are not cold.

How to decorate the final class DocumentGenerator

I am having problems to decorate the final class "DocumentGenerator" (in vendor/shopware/core/Checkout/Document/Service/DocumentGenerator.php) and overwrite the "generate" function inside of it.
I tried to decorate it the usual way, but an error is thrown because the "DocumentController" class excepts the original class and not my decorated one?
Argument 2 passed to Shopware\Core\Checkout\Document\DocumentGeneratorController::__construct() must be an instance of Shopware\Core\Checkout\Document\Service\DocumentGenerator
Its also not possible to extend from the class in my decorated class, because the "DocumentGenerator" is a final class.
My goal is to execute additional code, after an order document is generated. Previously I successfully used to decorate the "DocumentService" Class, but its marked as deprecated and shouldnt be used anymore. Also the "DocumentGenerator" class is used for the new "bulkedit" function for documents as of Version 6.4.14.0
I'm grateful for every tip.
As #j_elfering already wrote it's by design that you should not extend that class and therefore also shouldn't decorate it.
To offer a potential alternative:
Depending on what you want to do after a document has been generated it might be enough to add a subscriber to listen to document.written, check if it was a new document created and then work with the data from the payload for fetching/persisting data depending on that.
public static function getSubscribedEvents()
{
return [
'document.written' => 'onDocumentWritten',
];
}
public function onDocumentWritten(EntityWrittenEvent $event): void
{
foreach ($event->getWriteResults() as $result) {
if ($result->getOperation() !== EntityWriteResult::OPERATION_INSERT) {
// skip if the it's not a new document created
continue;
}
$payload = $result->getPayload();
// do something with the payload
}
}
Probably not what you want to hear but: The service is final in purpose as it is not intended to be decorated.
So the simple answer is you can't. Depending on your use case there may be other ways that don't rely on decoration.

Should operations on aggregate root that cause changes in child entities raise the child domain events?

Let's say that we have an Entity (the Root) with a property identifier, and another Entity (a child / local entity to Root) with a property name, with the rule that the Child's name must start with the Root identifier. There is a changeIdentifier operation and a renameChild operation, and the Child::name property must always be consistent with the Root identifier.
The renameChild operation, after checking the validity of the new name, raises a ChildRenamed domain event, containing the Root and the Child identities and the new name value.
The chengeIdentifier operation changes the Root's identifier and raises a RootIdentifierChanged event, then goes on and changes all the Child's names. What should happen now? Should ChildRenamed events be raised or not? And are importantly, why? What is the rationale behind one choice or the other? I guess the answer also depends on the event being raised from the Root or the Child.
class Root extends AggregateRoot {
private GUID _identity
private name _identifier
private Collection<Child> _children
function changeIdentifier(newIdentifier: string): void {
oldIdentifier = _identifier
_identifier = newIdentifier
addDomainEvent(new RootIdentifierChanged(_identity, _identifier))
// this...
// raises `ChildRenamed` events
for (child in _children) {
newName = child.name().replace(oldIdentifier, _identifier, STR_START)
renameChild(child.identity(), newName)
}
// ... or this
// no `ChildRenamed` events raised
for (child in _children) {
newName = child.name().replace(oldIdentifier, _identifier, STR_START)
child.rename(newName)
}
// ... or event this?
// `ChildRenamed` events raised or not depending on
// `renameAfterRootIdentifierChange` implementation
for (child in _children) {
child.renameAfterRootIdentifierChange(_identifier)
}
}
function renameChild(childIdentity: GUID, newName: string): void {
checkRule(new ChildNameIsConsistent(_identifier, newName))
child = _children.find(fn(child) => child.identity().is(childIdentity))
child.rename(newName) // the event is not raised here, in the child...
// ... but here, in the root
addDomainEvent(new ChildRenamed(_identity, child.identity(), newName))
}
}
From my experience the best way is to collect all events from all child entities in the aggregate root and make this list of raised events available from the aggregate.
With that the raising/creation of the events and the actual dispatching of the events to the subscribing components can be separated.
This allows to not dispatch any events if something along the business transaction goes wrong on the way.
I also often use a similar approach for collecting errors raised by child entities as outlined in this answer: https://stackoverflow.com/a/60863098/7730554
Update in order to take the comment into account:
I prefer using a base domain entity class which provides the functionality to add, merge and query the events which is used by both aggregates and entities to avoid code duplication.
From my point-of-view, an aggregate has to know about it's children anyway. If, in some situations the child entity has a child entity by itself (which I try to avoid if possible to avoid deep trees of children and making aggregates too big and complex) it can use the same functionality to query it's child events again.
With this approach no entity - be it the aggregate root or any child entity - needs to know where to push events to and does not have to care about who and when will query the list of events.

How do I get a parameter to not just display in a component, but also be recognized inside of OnInitializedAsync()?

I'm working on a blazor server-side project and I have a component that gets passed a model (pickedWeek) as a parameter. I can use the model fine in-line with the html, but OnInitializedAsync always thinks that the model is null.
I have passed native types in as parameters, from the Page into a component, this way without an issue. I use a NullWeek as a default parameter, so the number getting used in OnInitializedAsync only ever appears to be from the NullWeek. In case this is related, there is a sibling component that is returning the Week model to the Page through an .InvokeAsync call, where StateHasChanged() is being called after the update. It appears that the new Week is getting updated on the problem component, but that OnInitializeAsync() either doesn't see it, or just never fires again- which maybe is my problem, but I didn't think it worked that way.
For instance, the below code will always show "FAILURE" but it will show the correct Week.Number. Code below:
<div>#pickedWeek.Number</div>
#if(dataFromService != null)
{
<div>SUCCESS</div>
}
else
{
<div>FAILURE</div>
}
#code{
[Parameter]
public Week pickedWeek { get; set; }
protected IEnumerable<AnotherModel> dataFromService { get; set; }
protected override async Task OnInitializedAsync()
{
if (pickedWeek.Number > 0)
{
dataFromService = await _injectedService.MakeACall(pickedWeek.Id);
}
}
}
#robsta has this correct in the comments, you can use OnParametersSet for this. Then, you will run into another issue, in that each rerender will set your parameters again and generate another call to your service. I've gotten around this by using a flag field along with the the OnParametersSet method. Give this a shot and report back.
private bool firstRender = true;
protected override async Task OnParametersSetAsync()
{
if (pickedWeek.Number > 0 && firstRender)
{
dataFromService = await _injectedService.MakeACall(pickedWeek.Id);
firstRender = false;
// MAYBE call this if it doesn't work without
StateHasChanged();
}
}
Another alternative is to use the OnAfterRender override, which supplies a firstRender bool in the the method signature, and you can do similar logic. I tend to prefer the first way though, as this second way allows it to render, THEN sets the value of your list, THEN causes another rerender, which seems like more chatter than is needed to me. However if your task is long running, use this second version and build up a loading message to display while the list is null, and another to display if the service call fails. "FAILURE" is a bit misleading as you have it as it's being displayed before the call completes.
I've also found that a call to await Task.Delay(1); placed before your service call can be useful in that it breaks the UI thread loose from the service call awaiter and allows your app to render in a loading state until the data comes back.

Resources