3

If I have a domain class with an instance method Delete(). What is the best way to ensure that any leftover references to an instance that has "deleted itself" are subsequently unusable?

This question is very similar to mine, but I think the OP is more broadly concerned with how the API as a whole should be notified of deletions that occur elsewhere and react to such deletions. Any mention of how the deleted instances should behave is merely peripheral.

For instance, one bullet point says:

If my wrapper object finds out that its unerlying [sic] storage has been deleted (e.g. because of access to an uncached property returned an error), should it now mark itself as Deleted (so that all access to properties and methods return some sort of ObjectDeletedException) or should it just clear all caches and allow future calls to fail naturally.

I'm more concerned with how to implement the failure that should occur when properties or methods of the deleted instance are used. I can put a gateway check inside each property and method that throws an exception (as stated in the quote above), but that would create extremely brittle code since then every future developer -- including me -- would have to remember to add that check when creating new properties or methods.

Furthermore, I don't feel that such checks would be enough, as the mere presence of an object reference could be meaningful, regardless of its underlying properties or methods. For example, a client of my API may hold references to these instances they believe exist, and if one has deleted itself, that instance should no longer be valid and should cause all sorts of hell. Maybe it could just raise a "NotifyDeleted" event and leave it up to the client to respond, but I'm not sure if that's sufficient.

3
  • 1
    Not sure if I follow, but it sounds like you need some sort of observer/subscriber, in order to do the "notify" Oct 7, 2015 at 14:49
  • You mention both "domain object" (may exist several objects) and "domain class" (a class is treated as a single object or only one instance of a single class). Can you edit your question an clarify this ?
    – umlcat
    Oct 7, 2015 at 16:32
  • @umlcat -- Thanks for the feedback. Please see my edits.
    – rory.ap
    Oct 7, 2015 at 17:49

3 Answers 3

1

Are we talking about classes and instances (as per first para) or objects and references (as per title)?

In both cases, you either:

Keep the deleted item (though marked as deleted), then disallow accesses to the deleted item. Occasionally, you'll have to determine when deleted items no longer have any references so you can release/free them, or else you'll have a memory leak.

-or-

You find all usages (instances or references) and delete them as well as part of the item deletion. You'll have to do this recursively, and either atomically or deleting references first, then items.

-or-

Disallow the deletion because of outstanding uses, forcing the user or client to track them down and delete them first.

I would work this problem from the client-side experience to see what the domain users expect.

Which ever solution you use, there are techniques that make these things easier. The problem/solution spaces here are similar to memory management, (alloc/free), garbage collection, and referential integrity management (databases).

Reference counts work will if all you need is to know if there is a reference, and, your domain precludes cycles. However, backward references may be helpful so you can more quickly find the references; tracing your structures like GC's do is another option.

Still yet, you can use a SQL database and take advantage of cascading deletions.

In any case, I think the domain experience is the most important, and I'd start with design requirements from that perspective, then pick among the various solutions that meet your business/domain needs.

For example, do you want a trash-can-like effect where you can support un-delete, or undo? Do you want to keep objects that have references (to deleted items), but clear or null out the reference? Should the whole referring objects also be deleted with no further ado? Do your clients/users expect to be able to ask "what are the items that refer to this item" (just first level, or, possibly recursively in one query)? What are the implications on multi-user editing of the same space (that an object one user is aware of is delete by another user).

0

Your aggregate should be taking care of cleaning up any references between the entities underneath it. So that really just leaves problems with aggregates referencing other aggregates.

Removing the aggregate references from other aggregates can be taken care of with a Process Manager... a component that listens for specific events and in turn issues commands. Among other things, it's a way to make things happen across aggregates without having a transaction involving multiple aggregates (which limits scalability and/or performance).

Technically, you could remove aggregate-to-aggregate references from the client just after you successfully delete the aggregate. But this requires a level of trust (both in intention and handling it correctly) in the client that you may not have or want to assume going forward.

In the past when I needed the deleted object to respond "I'm sorry Dave. I'm afraid I can't do that.", I have had the repository return an interface. (Many people do this anyway as a matter of course.)

public interface IMyDomainThing
{
    void Method1();
    void Method2();
    string Property1 { get; }
}

// repo method
public IMyDomainThing GetMyDomainThing(Guid id);

Then the repository itself checked whether the item was deleted and returns a deleted version in that case:

public DeletedMyDomainThing: IMyDomainThing
{
    private void RaiseError()
    {
        throw new NotSupportedException("Not allowed on a deleted object");
    }

    public void Method1() { RaiseError(); }
    public void Method2() { RaiseError(); }
    // not entirely sure this is necessary
    public string Property1 { get { RaiseError(); } }
}

Yes, the maintenance on this sucks, because changing the public members requires you to touch the interface, non-deleted class, and deleted class. However, I wouldn't call it brittle, because the interface gives you compile-time (and in VS, design-time) checking to make sure that your deleted version has everything filled in.

0

Short Quick Answer

There are several techniques to detect when there are several references to the same [domain] object, and the object is removed.

Long Boring Answer

There are several techniques to clear several references of the same object.

And those techniques can be combined.

[1] Always use a pointer or reference

Since you object is conceptually "shared" by other objects, use a "reference" or "pointer" in all of them.

You did not mention which Programming Language you were applying your question. In some P.L.'s "pointers" are used, "references" are used.

In some P.L.'s like C++ or Object Pascal, a object variable can be declared in two ways:

[1.1] Direct (variable "statically allocated")

[1.2] Or using a "pointer" ("dynamic variables")

And the syntax for each ways are different.

In "reference" P.L. both concepts are kind of mixed.

Many programmers use a standard variable, or non pointer variable, the first time, and pointers, later, use always a pointer or reference.

[2] Use the "Observer" (a.k.a. "Publisher-Subscriber") Design Pattern

As @Christopher Francisco, mention you may want to use the "Observer" Software Design Pattern, a.k.a. "Publisher-Subscriber", a.k.a. "Client-Server".

Wikipedia Info:

https://en.wikipedia.org/wiki/Observer_pattern

https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern

Basically, when an object wants to do something with itself, has a preregistered set of references to other objects, that notifies changes.

In this case, the object to be removed, notifies the containers or owners of the references, not the references itselves, that its going to be removed.

[3] Use "BeforeEvent", "ExecuteEvent", "AfterEvent" pattern

This one is usually confused with the "Observer" Design Pattern, because it's mixed with it, many times.

You will se this a lot in G.U.I. controls.

Basically means, that an object may split some important actions in 3, "before", "while", and "after". Sometimes the "before" includes doing a confirmation of the action, and if the action is cancelled, the other 2 methods are not executed.

So this:

...........................
..+---------------------+..
..|       SomeClass     |..
..+---------------------+..
..| [+] Delete()        |..
..+---------------------+..
...........................

May become this:

...........................
..+---------------------+..
..|      SomeClass      |..
..+---------------------+..
..| [#] BeforeDelete()  |..
..| [#] ExecuteDelete() |..
..| [#] AfterDelete()   |..
..+---------------------+..
..| [+] Delete()        |..
..+---------------------+..
...........................

And, of course, the public Delete method calls the other methods.

When mixed with the "Observer" Design Pattern, in each action, a notification is sent to each subscriber object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.