about software programming techniques


JJ van Zon 2023

🤖 Business Logic Patterns

back

Business logic guards the rules of a system: the invisible part of the software.
It sits between the presentation and the data layer.

Contents

Introduction

Presentation, Entity model and persistence should be straightforward pattern-wise. If anything ‘special’ needs to happen it belongs in the business layer. Any number of different patterns can be used. But also things, that do not follow any standard pattern.

The business layer externally speaks a language of Entities or sometimes DTO's. Internally it can talk to Repository interfaces for data access.

It is preferred that business logic hooks up with Entity classes rather than Repositories. But there is a large gray area. Using Entities improves testability, limits queries and limits interdependence, dependency on a data source and passing around a lot of Repository variables.

RepositoryWrappers

Passing around lots of Repositories can create long lists of parameters, prone to change. To prevent that phenomenon, sets of Repositories could be combined into RepositoryWrappers. Those can then be passed around instead. This keeps the parameter lists shorter.

You can make a single RepositoryWrapper with all the Repositories out of a functional domains in it.

Some logic might use Repositories out of multiple domains. You could choose to pass around multiple RepositoryWrappers: one for each domain model. But you could also make a custom RepositoryWrapper with Repositories from multiple functional domains.

You may also want to more limited RepositoryWrappers. For instance one for each partial domain. This keeps the width of dependency more narrow, so logic that has nothing to do with certain Repositories, would not accidentally become dependent on them.

An alternative to RepositoryWrappers might be dependency injection. Under this link you can find some criticism about the techique, but that might be due to not using a very safe dependency injection API. RepositoryWrappers and dependency injection might also go hand in hand in combination with each other.

Validators

Separate Validator classes could be used for validation. Specialized classes can be derived from VersatileValidator from the JJ.Framework.

It is recommended to keep Validators independent from each other.

If multiple Validators should go off, you might call them individually one by one.

For complex Validator, it is suggested to add a prefix or suffix to the name such as Recursive or Versatile to make it clear that it is more than a simple Validator.

Next to Validators deciding whether user input is valid, Validators could also be used to generate warnings, that are not blocking, but help the user work with an app.

Validators might also be used for delete constraints. For instance when an Entity is still in use, you might not be able to delete it.

SideEffects

The business layer can execute SideEffects while altering data, for instance to record a date time modified, set default values, or automatically generate a name.

We could implement an interface ISideEffect for each of these. It has only one method: Execute. This gives us some polymorphism over SideEffects so it is easier to handle them generically and for instance Execute multiple in a row.

Using separate classes for SideEffects can create overview over pieces of logic, creative in nature, and prevent things from getting entangled.

SideEffects might evaluate conditions internally. The caller of the SideEffect class would not know what conditions there are. A SideEffect could skip over its own execution, when it wouldn’t apply. This makes the SideEffect fully responsible for what happens. What a SideEffect does can also depend on status flagging.

LinkTo

This pattern is about bidirectional relationship synchronization. That means that if a parent property is set: myProduct.Supplier = mySupplier, automatically the product is added to the child collection too: mySupplier.Products.Add(myProduct).

To manage bidirectional relationships, even when the underlying persistence technology doesn’t, we could link Entities together using LinkTo extension methods. By calling LinkTo, both ends of the relationship are updated. Here is a template for a LinkTo method that works for an 1-to-n relationship:

public static void LinkTo(this Child child, Parent parent)
{
    if (child == null) throw new NullException(() => child);

    if (child.Parent != null)
    {
        if (child.Parent.Children.Contains(child))
        {
            child.Parent.Children.Remove(child);
        }
    }

    child.Parent = parent;

    if (child.Parent != null)
    {
        if (!child.Parent.Children.Contains(child))
        {
            child.Parent.Children.Add(child);
        }
    }
}

Beware that all the checks and list operations can come with a performance penalty.

You could put the LinkTo methods together in a class named LinkToExtensions. You might put it in a LinkTo namespace in your project.

If a LinkTo method name becomes ambiguous, you could suffix it, for instance:

LinkToParentDocument

Next to LinkTo methods, you might also add Unlink methods in an UnlinkExtensions class:

public static void UnlinkParent(this Child child)
{
    if (child == null) throw new NullException(() => child);
    child.LinkTo((Parent)null);
}

NewLinkTo

If you are linking objects together, that you know are new, you may use better-performing variations for LinkTo, called NewLinkTo, that omit the expensive checks:

public static void NewLinkTo(this Child child, Parent parent)
{
    if (child == null) throw new NullException(() => child);
    child.Parent = parent;
    parent.Children.Add(child);
}

But beware that LinkTo might be a better choice, because executing NewLinkTo onto existing objects may corrupt the object graph.

Facade

A Facade combines several related (usually CRUD) operations into one class that also performs additional business logic and Validation, SideEffects, integrity constraints, conversions, etc. It delegates to other classes to do the work. If you do something using a Facade you should be able to count on it that integrity is maintained.

It is a combinator class: a Facade combines other (smaller) parts of the business layer into one, offering a single entry point for a lot of related operations. A Facade can be about a partial functional domain, so managing a set of Entity types together.

Repositories instead of Facades

Facades may typically contain CRUD operations, that could be used as an entry point for all your business logic and data access needs. But in some cases, it may be more appropriate to use the data access layer directly.

For example, a simple Get by ID may be better going through a Repository. There could be other cases where using Repositories directly is a better choice. For instance in the ToEntity and ToViewModel code, which is usually straightforward data conversion.

The reason is, that a Facade could create an excessive amount of dependency and high degree of coupling. Because simple operations executed frequently, would require a reference to a Facade, a combinator class, naturally dependent on many other objects. So, for a simple Get it may be better to use a Repository, to limit the interdependence between things.

Visitor

See the Visitors article.

Resource Strings

See the Resource Strings article.

back