Table of Contents

What are Interfaces?

Interfaces in programming are a way to define a contract for a class or a structure. They specify what a class must do, but not how it does it. They are used to define specific capabilities or behavior for classes which implement them.

A concrete example is when you want to ensure that a class has a certain method or property. By defining an interface and having your class implement it, you can guarantee that the class has the required method or property.

Interfaces in programming are a powerful tool for code sharing. Here are some advantages:

  1. Polymorphism: Interfaces allow for polymorphism, which is the ability of an object to take on many forms. An object of a class that implements an interface can be treated as an object of that interface. This allows you to write code that can work with objects from different classes, as long as they implement the same interface.

  2. Programming Contract: An interface defines a "contract" that the implementing classes must adhere to. This contract stipulates what methods a class must have, but not how they should be implemented. This ensures a certain level of consistency across classes.

  3. Substitution: Interfaces allow for substitution, meaning an object of a class that implements an interface can be replaced with an object of another class that implements the same interface, without affecting the functioning of the code.

  4. Code Reusability: Interfaces allow for code reusability without the need for inheritance. This can make the code easier to understand and maintain.

  5. Dependency on Abstractions: Interfaces promote dependency on abstractions, not on concrete classes. This makes the code more flexible and easier to test and maintain.

Note

For the moment, Neos only allows you to create interfaces to be applied to entity views. You can define properties on the interface.

How to create an Interface

To create an interface, you can click in the menu on the + button of the Interfaces item under one of these folders of a module:

  • Shared folder for interfaces used in both backend and frontend
  • Backend folder for interfaces used only in backend code
  • Frontend folder for interfaces used only in frontend code

The folder where you create the interface will determine its default scope, but you can still modify the scope later if needed.

Name

This is the name of the interface. It is common practice to start the name of an interface with an "I".

Scope

This is the scope of the interface. By default the scope is Shared. This means that the interface can be used in frontend and backend code. You can specify the following scopes:

  • Shared: The interface can be used in both frontend and backend code.
  • Backend: The interface can only be used in backend code. No frontend code is generated for this interface.
  • Frontend: The interface can only be used in frontend code. No backend code is generated for this interface. C# code is only generated for the abstraction.

Switching scopes

In Neos Studio, you can switch the scope of an interface by using the Change scope action.

Shared to Backend

If you switch the scope from Shared to Backend, the interface will only be available in backend code. When the application is generated, if the interface is used in frontend code, you will get a generation error.

Shared to Frontend

If you switch the scope from Shared to Frontend, the interface will only be available in frontend code. When the application is compiled, if the interface is used in backend code, you will get a compilation error.

Important

The namespace of the abstraction class generated is different depending on the scope:

  • For Shared and Backend, the namespace is {RootNamespace}.Domain.Interfaces or {RootNamespace}.Application.Abstractions.Interfaces.
  • For Frontend, the namespace is {RootNamespace}.CSharpAbstractions.Interfaces. A compilation errors in frontend code will occur when the scope is change from Shared to Frontend. Replace the namespace in your code {RootNamespace}.Application.Abstractions.Interfaces to {RootNamespace}.CSharpAbstractions.Interfaces to fix the compilation error.

Properties

Interfaces can declare properties that classes implementing the interface must have.

Name

This is the name of the property, which can be accessed by code (IMyInterface.MyProperty).

DotNet data type

This is the C# type used for the interface property.

Note

For the moment, properties are generated with a getter and a setter for scalar properties. If the Dotnet data type is a list then the property is generated with only a getter.

How to use Interfaces

In entities

To use an interface on a entity, you need to click in the entity menu on the + button of the Implemented interfaces item. You can then add an implemented interface to the grid using the lookup component.

Note

For the moment, no check is performed at generation time. You will get a compilation error if the entity does not implement the interface correctly.

Interfaces can be used by having entities implement them. An entity that implements an interface must provide an implementation for all the properties declared in the interface.

Note

For the moment, mapping (when entity property names are different) is not supported.

For the following example, consider an interface named IPartyRole. It has a Name property with string C# type. Two entities (Customer, Supplier) have a Name property.

To specify that Customer and Supplier implement the IPartyRole interface, open the entities in Neos Studio and click on the + of the Implemented interfaces treeview and add the specified implemented interface.

In your code

In the example, if you have a common business rule on the PartyRole.Name property you can create a method with a parameter type IPartyRole.

Example of a class that checks the name.

public class PartyRoleHelper : IPartyRoleHelper
{
    public bool IsValidName(IPartyRole partyRole)
    {
        // Add your validation logic here. For example:
        return !string.IsNullOrWhiteSpace(partyRole.Name);
    }
}

So, in your Customer and Supplier validation rule, you can inject IPartyRoleHelper to call IsValidName.

Example :

using GroupeIsa.Neos.Domain.Rules.ValidationRules;

namespace MyApp.Core.Domain.CustomerValidationRules
{
    public class NameCheck : ValidationRule<Customer>
    {
        private readonly IPartyRoleHelper _partyRoleHelper;

        public NameCheck(IPartyRoleHelper partyRoleHelper)
        {
            _partyRoleHelper = partyRoleHelper;
        }

        public override IValidationRuleResult Execute()
        {
            if (!_partyRoleHelper.IsValidName(Item))
            {
                return Error(string.Format(Domain.Properties.Resources.Core.InvalidName, Item.Name));
            }

            return Success();
        }
    }
}

A complete example of how to use UI view-level interfaces can be found in the TechnicalDemo cluster:

People > Shared > Interfaces > INamedObject

People > Backend > Entities > ContactInformation > Implemented interfaces > INamedObject

People > Backend > Entities > ContactInformation > Validation rules > CheckName

People > Backend > Entities > Organization > Implemented interfaces > INamedObject

People > Backend > Entities > Organization > Validation rules > CheckName

People > Backend > Entities > Person > Implemented interfaces > INamedObject

People > Backend > Entities > Person > Validation rules > CheckName

In entity views

To use an interface on a entity view, you need to click in the entity view menu on the + button of the Implemented interfaces item. You can then add an implemented interface to the grid using the lookup component.

Note

For the moment, no check is performed at generation time. You will get a compilation error if the entity view does not implement the interface correctly.

Interfaces can be used by having entity views implement them. An entity view that implements an interface must provide an implementation for all the properties declared in the interface.

Note

For the moment, mapping (when entity view property names are different) is not supported.

In your code

It's the same as for entities. As a best practice, we advise you to set up your business rules and processes at entity level (domain layer). This makes them easier to reuse, and they will always be called even if there are new entity views.

In UI views

To use an interface on a UI view, you need to click in the UI view menu on the + button of the Implemented interfaces item. You can then add an implemented interface to the grid using the lookup component.

Note

For the moment, no check is performed at generation time. You will get a compilation error if the UI view does not implement the interface correctly.

Interfaces can be used by having UI views implement them. An UI view that implements an interface must provide an implementation for all the properties declared in the interface.

Note

For the moment, mapping (when entity view property names are different) is not supported.

In your code

In principle, it's the same as server-side, except that the implementation is client-side. So you can use a UI method that takes an interface as parameter to reuse the code.

A complete example of how to use UI view-level interfaces can be found in the TechnicalDemo cluster:

People > Shared > Interfaces > INamedObject

People > Frontend > UI views > ContactInformationUI > Implemented interfaces > INamedObject

People > Frontend > UI views > ContactInformationUI > Pre-validation rules > CheckName

People > Frontend > UI views > OrganizationUI > Implemented interfaces > INamedObject

People > Frontend > UI views > OrganizationUI > Pre-validation rules > CheckName

People > Frontend > UI views > PersonUI > Implemented interfaces > INamedObject

People > Frontend > UI views > PersonUI > Pre-validation rules > CheckName

People > Frontend > UI methods > NameChecker.IsValid