Is it a problem with my architecture?

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 "little things". However, in practice and due to some particularities of my business rules, I feel a little difficult to apply some concepts. I would appreciate it if you could help me with these issues.

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

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

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

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 address are:

  • The User class exposes the UserPassword property that, despite having its content encrypted, I would not like to expose it to the final developers, so that it was an internal property. But how do you do this if in the Repository project you need to have access to this property so you can map it to its column in the database table?

  • As you can see, the User class has a navigation property for the Membership entity, in a one-to-many relationship. A Membership , in turn, has two navigation properties (one for User and another for Role ), so 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 by the User.GetPasswordRules() method. So far so good. The problem is that a certain user will not necessarily be able to access the Roles, in the same way that someone who has access to the role register does not necessarily have access to the users. Therefore, Membership should not expose its browsing properties. But if I remove the User and Role navigation properties from Membership , how can I get the functionality of the User.GetPasswordRules() method since the Domain project does not access the database?

  • There are a few other issues, but I think these two are the main ones. I hope I have been clear. Thanks for any help.

        
    asked by anonymous 04.01.2017 / 18:08

    1 answer

    0

    RESOLVED !!

    I was able to solve my two issues with a single solution.

    According to the answer obtained by another post from me (#) I could use the InternalsVisibleToAttribute assembly attribute by establishing a relation trust between Domain and Repository and assign the internal modifier to members and classes that I want to hide from other assemblies.

    So, my classes look 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 include the InternalsVisibleToAttibute attribute for Domain :

    Domain/Properties/AssemblyInfo.cs

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

    In this way, the Repository project gains access to the internal members of Domain as if they were public, thus enabling the mapping of each property by EF Fluent API without problem some.

    REFERENCES

  • InternalsVisibleToAttribute Class
  • Friend Assemblies (C # and Visual Basic)
  • 05.01.2017 / 19:15