Architecture Question

5

Estou escrevendo um módulo de regras / elegibilidade. Eu tenho dois conjuntos de dados, um é os dados do cliente e o outro é os dados dos produtos do cliente. Os dados do cliente para os dados dos produtos do cliente são de um para muitos.

Agora, tenho que passar por um conjunto de regras de elegibilidade para cada um desses dados de produto do cliente. Para cada dado de produtos do cliente, posso dizer que o cliente está qualificado para o produto ou declina a elegibilidade e deve passar para o próximo registro de produto.

Portanto, em todas as regras, preciso ter acesso aos dados do cliente e do cliente (o registro específico em que as regras estão sendo executadas). Como todas as regras podem aprovar um produto ou recusar um produto, criei uma interface com esses dois métodos e estou implementando a interface para todas as regras. Estou passando os dados do Cliente e os dados de um produto para todas as regras (porque as regras devem ser executadas em cada linha de dados do produto do cliente). Uma situação Ideal seria disponibilizar os dados de produtos de clientes e clientes para a regra, em vez de transmiti-los para cada regra.

Qual é a melhor maneira de fazer isso em termos de arquitetura?

Edit: Aqui está o que estou fazendo

public class CustomerContract
{
    public DateTime StartDate { get; set; }
    public DateTime endDate { get; set; }
    //Other Contract related details

}
public class Customer
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string state { get; set; }
}

public class CustomerInfo
{
    public CustomerInfo(int CustomerID)
    {
        Customer = new Customer();// Get Customer from DB
        AppliedProducts = new List<CustomerProduct>();// Get the customer products by customerID
        CurrentContract = new CustomerContract();//Get the contract by CustomerID, State
        declineReasons = new List<int>();// just intailizing. the decline codes are added by rules.
    }

    public CustomerContract CurrentContract { get; set; }
    public IList<CustomerProduct> AppliedProducts { get; set; }
    public IList<int> declineReasons { get; set; }
    public Customer Customer { get; set; }
}

public class CustomerProduct
{
    public decimal AmountCharged { get; set; }
    public int DeclineReasonID { get; set; }
    public int Product { get; set; }
    public decimal DiscountApplicable { get; set; }
    public decimal AmountQuoted { get; set; }
    public decimal Tax { get; set; }
}

public interface IRule
{
    // Since we need to have access to contracts when deciding the eligibility
    CustomerProduct ExecuteRule(CustomerProduct currentproduct, CustomerInfo customerInfo);

    //when denied, 
    CustomerProduct DenyProduct(CustomerProduct currentProduct);
}

public class EligibityEngine
{
    List<IRule>  rules = new List<IRule>();
    private CustomerInfo c;

    public EligibityEngine(int CustomerID)
    {
        c = new CustomerInfo(CustomerID);
        LoadRules();
        foreach (var customerProduct in c.AppliedProducts)
        {
            ExecuteRules(customerProduct);
        }
    }

    private void ExecuteRules(CustomerProduct currentProductItem)
    {
        foreach (var rule in rules)
        {
            currentProductItem = rule.ExecuteRule(currentProductItem, c);
        }
    }

    private void LoadRules()
    {
        // add all the IRule types here
        //rules.Add();
    }
}

Quando executo a regra, passo os dados do produto do cliente e os dados do cliente. Eu tenho que passar o resultado de uma execução de regra para o outro. No padrão acima, se a regra alterar dados na classe customerInfo, ela não será salva, a menos que seja aprovada como ref.

Minha pergunta é que eu quero que todas as regras tenham acesso a esses dados sem passar por eles. Então, não preciso me preocupar em capturar a saída da regra.

Eu quero que o método executeRule seja cancelado.

    
por katie77 25.11.2011 / 00:56
fonte

3 respostas

3

O padrão de especificação parece se adequar ao seu precisa muito bem. O código de exemplo fornecido é precisamente sobre clientes e produtos.

    
por 25.11.2011 / 12:34
fonte
0

Eu não tenho certeza se esta resposta vai ajudar, mas eu dou uma chance de qualquer jeito.

Com base na minha compreensão do que você está dizendo, aqui está minha sugestão:

Você já tem um módulo que fornece acesso aos dados do cliente e possui um módulo que fornece acesso aos dados do produto do cliente. Para cada um desses módulos, defina uma (ou mais) interfaces para acessar os dados que serão usados por pelo menos uma regra.

Em seguida, crie uma interface 'IRuleContext' que tenha métodos para obter as interfaces para acessar os dados do cliente e os dados do produto do cliente.

Escreva um módulo "Regras" de forma que cada regra receba um parâmetro do tipo 'IRuleContext'. Por exemplo, você poderia ter uma classe base abstrata 'EligibilityRules' com um método abstrato de 'bool AppliesTo (IRuleContext)'. Então você poderia implementar um número de regras concretas com cada uma delas usando um algoritmo diferente para determinar a elegibilidade.

Em vez de módulos, você também pode ter serviços, componentes, classes, etc. Isso depende da tecnologia que você está usando. Além disso, certifique-se de que suas opções atendem aos seus requisitos, por exemplo, um serviço da Web pode não ser uma opção em seu cenário.

    
por 25.11.2011 / 04:28
fonte
0

Como @ ian31 apontou, você precisa do padrão de especificação. Uma vez eu tive uma necessidade muito semelhante e o design que eu criei é mais ou menos como segue:

  • Cada regra de negócio (regras de elegibilidade, no seu caso) foi implementada como um predicado lógico ou especificação.
  • Como no seu caso, havia cadeias de regras, que poderiam ser organizadas em uma conjunção (operação AND) ou em uma disjunção (operação OR). Em uma disjunção, o parâmetro teve que ser aceito por pelo menos uma das regras e essa regra deve ser relatada pelo mecanismo de regras. Em uma conjunção, o parâmetro deve ser aceito por todas as regras, caso em que a primeira regra na cadeia deve ser relatada.
  • Havia uma interface Rule que tinha três implementações importantes: uma que delegava a um predicado e as outras duas implementavam os requisitos de conjunção e disjunção descritos acima.
  • Houve um RuleFactory que organizou as regras na ordem especificada pelo cliente (o que está sendo feito pelo método LoadRules() em seu exemplo). Esse pedido pode ser facilmente alterado sem afetar qualquer outra parte do aplicativo.

É importante notar que nenhuma dessas classes mudou de estado. Eles eram funções puras que aceitavam ou rejeitavam dados.

    
por 26.11.2011 / 16:53
fonte