Business logic guards the rules of a system: the invisible part of the software.
It sits between the presentation and the data layer.
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.
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.
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.
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.
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);
}
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.
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.
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.
See the Visitors
article.
See the Resource Strings
article.