mercredi 16 juin 2021

Which Design pattern (C#) will make the implementation more repeatable, scalable, clean and testable (in that order of priority)?

New Design:

We are porting Legacy code to C#.

We have Request and Response Data Contract for each set of Business Data. These are POCO generated using Schema of External vendor. We will be creating 30-40 such Classes. We have no control over the structure. Hence I extended these classes with ToModel() and FromModel() methods to implement mapping functionality between Contract and Models explained below (sort of Adapter pattern)

Similarly we have Request and Response UI Models for each Business Screen. Ui Models are based on Legacy models.

There is an External API service that only accepts these Data Contract structures. So Business Service has the responsibility of mapping request UI Model to request Contract, Sending it to External API, getting response contract, mapping it to UI response model

Problem statement: We have written 2 methods (eg:GetBiz1Data() is one of them) in 2 Business Services so far and when I looked at them they looked exactly the same. The difference in both methods was the Classes for DataContract, RequestUiModel and ResponseUiModel

Existing Design Constraints:

  1. The different request and response DataContracts are generated proxy classes based on external vendor schema. They may or may not have any fields in common.

  2. The different request and response UiModels may or may not have anything in common.

  3. Since this is a migration of legacy code to NET Core and the legacy code structure is still relevant, we have to follow the same Ui models as of legacy code.

What I am looking for:

Is there are way I can make the BusinessService and hence GetBiz1Data generic so that BusinessService1, BusinessService2 and so on will be able to just inject their Data Contracts and Models and not re-code the algorithm? Can the ToModel() and From Model() be refactored to another pattenrn tied to the Business Service?

I need this to make the code clean, avoid redundant algorithm in method and is scalable with future Business Service implementations.

Code:

    [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v13.0.0.0)")]
    public class ResponseDataContract1 
    {
        [Newtonsoft.Json.JsonProperty("ServerResponse", Required = Newtonsoft.Json.Required.Always)]
        public ServerResponse ServerResponse { get; set; } = new ServerResponse();
    
        private System.Collections.Generic.IDictionary<string, object> _additionalProperties = new System.Collections.Generic.Dictionary<string, object>();
    
        [Newtonsoft.Json.JsonExtensionData]
        public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
        {
            get { return _additionalProperties; }
            set { _additionalProperties = value; }
        }
    }

    [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v13.0.0.0)")]
    public partial class ServerResponse 
    {
        [Newtonsoft.Json.JsonProperty("contract_field1", Required = Newtonsoft.Json.Required.Always)]
        public string ContractField1 { get; set; }
    
        [Newtonsoft.Json.JsonProperty("contract_field2", Required = Newtonsoft.Json.Required.Always)]
        public string ContractField2 { get; set; }
    
        private System.Collections.Generic.IDictionary<string, object> _additionalProperties = new System.Collections.Generic.Dictionary<string, object>();
    
        [Newtonsoft.Json.JsonExtensionData]
        public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
        {
            get { return _additionalProperties; }
            set { _additionalProperties = value; }
        }
    }

    public static IResponseUiModel1 ToModel(this ResponseDataContract1 dataContract)
    {
    IResponseUiModel1 responseModel = ResponseUiModel1();
    responseModel.BusinessField1 = dataContract.ContractField1;
    responseModel.BusinessField2 = dataContract.ContractField2;
    }

    [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v13.0.0.0)")]
    public class RequestDataContract1 
    {
        [Newtonsoft.Json.JsonProperty("ServerRequest", Required = Newtonsoft.Json.Required.Always)]
        public ServerRequest ServerRequest { get; set; } = new ServerRequest();
    
        private System.Collections.Generic.IDictionary<string, object> _additionalProperties = new System.Collections.Generic.Dictionary<string, object>();
    
        [Newtonsoft.Json.JsonExtensionData]
        public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
        {
            get { return _additionalProperties; }
            set { _additionalProperties = value; }
        }
    }
    [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v13.0.0.0)")]
    public partial class ServerRequest 
    {
        [Newtonsoft.Json.JsonProperty("request_field1", Required = Newtonsoft.Json.Required.Always)]
        public string RequestField1 { get; set; }
    
        [Newtonsoft.Json.JsonProperty("request_field2", Required = Newtonsoft.Json.Required.Always)]
        public string RequestField2 { get; set; }
    
        private System.Collections.Generic.IDictionary<string, object> _additionalProperties = new System.Collections.Generic.Dictionary<string, object>();
    
        [Newtonsoft.Json.JsonExtensionData]
        public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
        {
            get { return _additionalProperties; }
            set { _additionalProperties = value; }
        }
    }

    public static void FromModel(this RequestDataContract1 dataContract, IRequestUiModel1 cmrRequestModel)
    {
    dataContract.RequestField1 = requestModel.BusinessField1;
    dataContract.RequestField2 = requestModel.BusinessField2;
    }

public interface IBusinessService1
    {
        /// <summary>
        public Task<IResponseUiModel1> GetBiz1Data<T>(IRequestUiModel1 requestModel) where T : Enum;
    }
    public class BusinessService1 : IBusinessService1
    {
 private IExternalApiService _externalApiService;
        private readonly ILogger<BusinessService1> _logger;

        public BusinessService1(IExternalApiService externalApiService,
 ILogger<BusinessService1> logger)
        {
            _externalApiService = externalApiService;
            _logger = logger;
        }


public async Task<IResponseUiModel1> GetBiz1Data<T>(IRequestUiModel1 requestModel) where T:Enum
        {
            IResponseUiModel1 responseModel;
responseModel.InputList = ListHelper.FetchEnumAsList<T>();

var requestDataContract = new RequestDataContract1();
requestDataContract.FromModel(requestModel);
var jsonRequestData = JsonConvert.SerializeObject(requestDataContract);

var responseContractData = await _externalApiService.SendData(jsonRequestData)

ResponseDataContract1 responseDataContract = JsonConvert.DeserializeObject<ResponseDataContract1>(responseContractData);

responseModel = responseDataContract.ToModel();

            return responseModel;
        }

    }

Similarly GetBiz2Data() method from BusinessService2 will look something like

public async Task<IResponseUiModel2> GetBiz2Data<T>(IRequestUiModel2 requestModel) where T:Enum
        {
            IResponseUiModel2 responseModel;
responseModel.InputList = ListHelper.FetchEnumAsList<T>();

var requestDataContract = new RequestDataContract2();
requestDataContract.FromModel(requestModel);
var jsonRequestData = JsonConvert.SerializeObject(requestDataContract);

var responseContractData = await _externalApiService.SendData(jsonRequestData)

ResponseDataContract2 responseDataContract = JsonConvert.DeserializeObject<ResponseDataContract2>(responseContractData);

responseModel = responseDataContract.ToModel();

            return responseModel;
        }

Aucun commentaire:

Enregistrer un commentaire