Is it a problem with my architecture?

Asked

Viewed 114 times

3

I’ve been a software developer for a long time, but I’m always looking to learn "different ways of doing things". I am currently working on a new project and decided to base the architecture on proposals suggested by the community using a combination of DDD, Entity Framework Code First and other "thingies" more. However, in practice and due to some particularities of my business rules, I feel a bit of difficulty in applying some concepts. I would appreciate it if you could help me with these questions.

This is the summary structure of my solution. All projects presented here are Class Library type:

  • Core - Contains basic functionalities for the whole system such as resource management, globalisation, etc.
    References:
    (...)
  • Domain - Contains the definition of entities and validations.
    References:
    Core (...)
  • Repository - Responsible for communication with the entity database and mapping through Fluent API.
    References:
    Core
    Domain
    Entity Framework 6
    (...)
  • Security - Applies the functionalities of the above projects to provide security features such as authentications and encryption.
    References:
    Core
    Domain
    Repository
    Entity Framework 6
    (...)

The project Domain contains a class called User defined by the following code (summarised):

namespace Domain.Entities
{
    public class User : EntityBase
    {
        private string _userCode;
        private string _userPassword;
        //outras variáveis....
        private int _customerId;

        [MaxLength(255), Required(AllowEmptyStrings = false)] //Estou utilizando Data Annotations para facilitar os testes de integridade e definir restrições básicas ao modelo.
        public string UserCode
        {
            get { return _userCode; }
            set
            {
                ValidateValue(GetType(), nameof(UserCode), value); //O método ValidadeValue está definido na classe base EntityBase e tem o propósito de validar o valor a ser atribuído à propriedade.
                _userCode = value;
            }
        }

        [MaxLength(255)]
        public string UserPassword
        {
            get { return _userPassword; }
            set
            {
                ValidateValue(GetType(), nameof(UserPassword), value);
                _userPassword = value;
            }
        }

        //outras propriedades...

        public int CustomerId
        {
            get { return _customerId; }
            set
            {
                ValidateValue(GetType(), nameof(CustomerId), value);
                _customerId = value;
            }
        }

        //Propriedades de navegação para outras entidades relacionadas
        public virtual Customer Customer { get; set; }
        public virtual ICollection<Membership> Memberships { get; set; }

        //Construtores
        public User() { }

        public User(string userCode, string userName, string email, Address address = null, bool isAdministrator = false)
        {
            UserCode = userCode;
            UserPassword = "";
            //demais inicializações...
            CustomerId = 0;
        }

        //outros métodos...

        ///<summary>
        ///Retorna as regras da política de senha vigentes ao usuário por meio de seus papéis.
        ///</summary>
        public PasswordRules GetPasswordRules()
        {
            var rules = (from r in Memberships.Select(x => x.Role)
                    where r.RulesPrecedence > 0
                    orderby r.RulesPrecedence
                    select r.PasswordRules).FirstOrDefault();
            return rules;
        }
    }
}

I’ll write another class here:

namespace Domain.Entities
{
    public class Membership : EntityBase
    {
        public int UserId { get; set; }
        public int RoleId { get; set; }

        public virtual User User { get; set; }
        public virtual Role Role { get; set; }

        public Membership() { }
    }
}

The questions I need to resolve are:

  1. The class User exposes the property UserPassword that, despite having its contents encrypted, I would not want to expose it to the ultimate developers, so that it was an internal property. However, how to do this if in the project Repository I need access to this property in order to map it to its column in the database table?

  2. As you can see, the class User has a navigation property for the entity Membership, in a one-to-many relationship. A Membership, in turn, has two navigation properties (one for User and another to Role), so that Membership makes a many-to-many relationship between User and Role. Through these relationships and properties, a user can know their password policy rules (defined in Role), as shown in the method User.GetPasswordRules(). So far, so good. The problem is that, a certain user will not necessarily be able to access the registration of papers (Roles), in the same way that someone who has access to the registration of papers will not necessarily have for the users. Therefore, Membership should not expose its browsing properties. But if I withdraw the browsing properties User and Role of Membership, how can I supply the functionality of the method User.GetPasswordRules() since the project Domain does not access database?

There are some other issues, but I think these two are the main ones. I hope I’ve been clear. Thanks for any help.

  • Look at that answer: http://answall.com/a/80696/61561

  • Good afternoon, have you thought about using ASP.NET Identity to take care of the authentication and authorization part in the application?

  • @Renancarlos, this is really a good discussion (use or not Repository Patterns on EF). However, I did not see how the answers of this post would help me. Unifying the Domain layer with Repository is not a possibility.

  • @Felipedeguchi, The Security layer performs authentication and authorization based on an existing business model and can be used on any platform (Windows, ASP.NET MVC, Webapi, Console, etc.) is not limited to the web infrastructure.

  • I don’t think it would be possible to leave the database connection layer in a Webapi and use DTO’s (Data Transfer Object) for when it is necessary to access data from User, so developers would only have access to the data you want them to have. The same goes for the second question, within a DTO you would have the Password rules without exposing the Membership.

1 answer

0


RESOLVED!!

I managed to solve my two questions with a single solution.

According to the answer obtained by another post of mine (It is possible to restrict who can use the public classes of an Assembly?) I was able to use the Assembly attribute InternalsVisibleToAttribute establishing a relationship of trust between Domain and Repository and assign the modifier internal members and classes I wish to hide from other assemblies.

So my classes were like this:

Domain.Entities.User

namespace Domain.Entities
{
    public class User : EntityBase
    {
        // código suprimido...

        [MaxLength(255)]
        internal string UserPassword  // modificado para internal
        {
            get { return _userPassword; }
            set
            {
                ValidateValue(GetType(), nameof(UserPassword), value);
                _userPassword = value;
            }
        }

        //outras propriedades...

        internal virtual Customer Customer { get; set; }   // modificado para internal
        public virtual ICollection<Membership> Memberships { get; set; }

        //Construtores
        internal User() { }    // modificado para internal para evitar a possibilidade de uma instância vazia.

        public User(string userCode, string userName, string email, Address address = null, bool isAdministrator = false)
        {
            UserPassword = "";
            //demais inicializações...
        }

        //outros métodos...
    }
}

Domain.Entities.Membership

namespace Domain.Entities
{
    public class Membership : EntityBase
    {
        public int UserId { get; set; }
        public int RoleId { get; set; }

        internal virtual User User { get; set; }  // modificado para internal
        internal virtual Role Role { get; set; }  // modificado para internal

        internal Membership() { }
    }
}

After defining the properties and constructors I want to protect, I included the attribute InternalsVisibleToAttibute for Domain:

Domain/Properties/AssemblyInfo.cs

[assembly: InternalsVisibleTo("Repository, PublicKey={chave pública de Repository}")]

Thus, the project Repository obtains access to the internal members of Domain as if these were public, thus enabling the mapping of each property by the EF Fluent API without any problem.


REFERENCES

  1. Internalsvisibletoattribute class
  2. Friend Assemblies (C# and Visual Basic)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.