{"id":20482209,"url":"https://github.com/rajagopal28/pricing-engine-go","last_synced_at":"2025-06-12T18:32:37.824Z","repository":{"id":88701870,"uuid":"389926186","full_name":"rajagopal28/pricing-engine-go","owner":"rajagopal28","description":"An Experimental application in goLang to work in a simple pricing engine considering various factors from the customer who is trying to rent a vehicle from a rental service provider.","archived":false,"fork":false,"pushed_at":"2021-08-03T09:10:15.000Z","size":224,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-05T15:51:30.623Z","etag":null,"topics":["chain-of-responsibility","configurable","go-testing","golang","linked-data","pricing","rest-api","rpc-server","service-oriented-architecture","strategy-pattern","stub-backend","tdd"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rajagopal28.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-27T09:37:17.000Z","updated_at":"2023-04-23T14:42:12.000Z","dependencies_parsed_at":null,"dependency_job_id":"3b974237-6dfd-4be7-944f-7736d8af8d1d","html_url":"https://github.com/rajagopal28/pricing-engine-go","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rajagopal28/pricing-engine-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rajagopal28%2Fpricing-engine-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rajagopal28%2Fpricing-engine-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rajagopal28%2Fpricing-engine-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rajagopal28%2Fpricing-engine-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rajagopal28","download_url":"https://codeload.github.com/rajagopal28/pricing-engine-go/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rajagopal28%2Fpricing-engine-go/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259519255,"owners_count":22870327,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chain-of-responsibility","configurable","go-testing","golang","linked-data","pricing","rest-api","rpc-server","service-oriented-architecture","strategy-pattern","stub-backend","tdd"],"created_at":"2024-11-15T16:12:04.041Z","updated_at":"2025-06-12T18:32:37.788Z","avatar_url":"https://github.com/rajagopal28.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pricing Engine - GoLang\nApplication in goLang to work in a simple pricing engine considering various factors from the customer who is trying to rent a vehicle from a rental service provider.\n\n\n## Requirements\nThe requirement of this application is to bring up a go based REST API which will determine the rental cost of a car bases on the attributes of the customer who is purchasing it. You can go through the detailed requirements [Here](./REQUIREMENTS.md). The essential attributes that play key aspects in the pricing calculation are as follows:\n- Rent Duration\n- Age of the customer\n- Insurance Group\n- Validity of the licence held by the customer\n\n\n## The Pricing formula\n\u003e Total Rental Cost = BaseFare based on the Duration * Factor based on Age * Factor based on the Insurance Group * Factor based on Licence Validity\n\n\n## Understanding the existing setup\nThe current application is built on top on an existing code as per the [Requirements](./REQUIREMENTS.md). The setup typically had a layered data flow among the components as follows:\n- The main.go file which typically invokes the service built on top of the RPC/Middleware libraries.\n- The service that serves various REST end point for the application and passing on the data to RPC layer.\n- The RPC which typically takes care of doing the conversion from HTTP middleware understandable data to the Application understandable data.\n- Then the RPC passes the application understandable data to the *App* component which has all the business logics of computing the pricing and serving the required data.\n\n\n## The Approach in solving the problem\n- Single responsibility Principle : Proper structuring of business logics to serve finer and granular purposes will help in greater lengths, hence I have split the data processing accordingly such that components like *ConfigFetcher*, *FactorMapper* and *RPC* have unique responsibilities that are really granular and the handing down of data to other actors also became really smooth and testable.\n- Generalisations and Specialisations : Bringing in *StrategyChain* and *Strategy* method reference was really fun. It totally minimised the boiler plate code by embracing the code reuse and carving out the skeleton that a lot of repeated code to repeatedly apply the subsequent pricing were really brought down to 3 lines as shown under *Chain of Commands*section below.\n- *Configurable and plugin code*: Decoupling the configuration from the implementation along with an `auto expiring cache` like approach is taken in this application considering the fact that fare prices or decision factors in most of the pricing related businesses tend to change often than we usually think.\n- Strategy Pattern :\n```go\n\tvar thirdStrategy = func( resp *pricingengine.PricingItem) (*pricingengine.PricingItem, error) {\n\t\tlog.Println(\"Processing LicenceValidityFactor Stragegy\")\n\t\treturn strategies.ApplySubsecuentFactorsToPricing(request, resp, licence_factor_range, nil) // final call so no next strategy\n\t}\n\n\tvar secondStrategy = func( resp *pricingengine.PricingItem) (*pricingengine.PricingItem, error) {\n\t\tlog.Println(\"Got Previous Strategy in InsuranceGroupFactor Stragegy data here::\", resp)\n\t\treturn strategies.ApplySubsecuentFactorsToPricing(request, resp, insurance_factor_range, thirdStrategy)\n\t}\n\n\tvar firstStrategy = func( resp *pricingengine.PricingItem) (*pricingengine.PricingItem, error) {\n\t\tlog.Println(\"Got Previous Strategy in DriverAgeFactor Stragegy data here::\", resp)\n\t\treturn strategies.ApplySubsecuentFactorsToPricing(request, resp, driver_factor_range, secondStrategy)\n\t}\n```\n- Chain of command pattern : One more interesting approach I chose to minimise the lines of code in implementation is to simplify the way the incremental factors are applied on the base price. This model is completely dependant on the configurable model and is totally extendable with really less implementation code. All credits goes to the *method references*/`Function as a data type` approach supported by Go which helped a long way. The actual implementation of the Chain is completely upto the invoker, however, the chain will continue as long as there is a chain connected to the current one. Code snippet below:\n```go\nif fn != nil {\n  log.Println(\"Found a chain function, Passing on the result for further computation\")\n  return fn(\u0026result)\n}\n```\n- *Functional Decomposition* : As a bi-product of applying the *Single responsibility Principle*, `Functionally decomposing` the data flow became really easy considering the fact that each actor has a `defined`, `highly cohesive` data flow within itself and `low coupling` with other actors in the ecosystem.\n- Non-TDD/Implementation first : I am a hard core follower of *TDD* principles while developing any of the application or business logics. However, due to the lack of experience in go programming language I was not able to follow TDD approach 100%. During the initial spike/experimental faces I just started with the implementation and then jumped in to the respective unit test/functional test later.\n- *Test Pyramid*: I have followed test pyramid starting from *UnitTests --\u003e FunctionalTest --\u003e IntegrationTest* for this project such that all the scenarios are covered.\n - Integration testing with `httptest`\n - Unit test with `testing` framework\n- *Dedicated logging*: Logging is one of the crucially best practices among all programming languages. I have used a simplistic logging approach with GoLang's `logging` package.\n- The request response structures: As part of the design it was pointed out to be extra cautious on designing the Request/Response object. Below is the way the GeneratePricing Request and Response object fields are classified.\n```go\ntype GeneratePricingRequest struct {\n  DateOfBirth string `json:\"date_of_birth\"`\n  InsuranceGroup int `json:\"insurance_group\"`\n  LicenseHeldSince string `json:\"license_held_since\"`\n}\n\ntype GeneratePricingResponse struct {\n\tInput GeneratePricingRequest `json:\"input\"`\n  IsEligible bool `json:\"is-eligible\"`\n  Message string `json:\"message\"`\n  PricingList []PricingItem `json:\"pricing\"`\n}\n\ntype PricingItem struct {\n\tPremium float64 `json:\"premium\"`\n  Currency string  `json:\"currency\"`\n  FareGroup string `json:\"fare_group\"`\n}\n\n```\n\n## The Flow\n## Architecture:\n![DataFlow](./images/PricingEngineFlowDiagram.png \"DataFlow\")\n\n\n## The Endpoints\n### Generate Pricing for a customer\n##### Request\n```http\nPOST /generate_pricing HTTP/1.1\nHost: localhost:3000\nContent-Type: application/json\n\n{\n    \"date_of_birth\": \"1970-12-04\",\n    \"insurance_group\": 12,\n    \"license_held_since\": \"1988-08-01\"\n}\n```\n### Request format:\n*date_of_birth* – Date of Birth of the customer who is trying to rent the vehicle\n*insurance_group* – the insurance group to which the customer belongs to\n*license_held_since* – The date of acquiring of the Driver's licence by the existing customer\n\n##### Response\nReturns: Empty body with one of the following:\n\n200 – in case of success\n\n\n```http\nHTTP/1.1 200 OK\nContent-Type: application/json\n{\n  \"input\": {\n          \"date_of_birth\": \"1970-12-04\",\n          \"insurance_group\": 12,\n          \"license_held_since\": \"1988-08-01\"\n      },\n      \"is-eligible\": true,\n      \"message\": \"Success\",\n      \"pricing\": [\n          {\n              \"premium\": 278.28254999999996,\n              \"currency\": \"£\",\n              \"fare_group\": \"0.5 hours, Driver Age \u003e26, Insurance Group:9-16, Licence Validity:6\"\n          },\n          {....},\n          {....},\n          .....\n      ]\n}\n\n```\n\n#### Get current pricing configuration ranges\n##### Request\n```http\nGET /generate_pricing HTTP/1.1\nHost: localhost:3000\n\n\n```\n\n##### Response\nReturns: The computed statistics for all the posted transaction or empty if there are no transactions present.\n\n```http\nHTTP/1.1 200\nContent-Type: application/json;charset=UTF-8\n{\n  \"base-rate\": [\n        {\n            \"Start\": 0,\n            \"End\": 1800,\n            \"IsEligible\": true,\n            \"Value\": 273,\n            \"Label\": \"0.5 hours\"\n        },\n        {....}\n        ....\n        ],\n    \"driver-age-factor\": [\n        {\n            \"Start\": 0,\n            \"End\": 16,\n            \"IsEligible\": false,\n            \"Value\": 0,\n            \"Label\": \"Driver Age:0-16\"\n        },\n        {...}\n        ....\n      ],\n      \"insurance-group-factor\": [\n          {\n              \"Start\": 1,\n              \"End\": 8,\n              \"IsEligible\": true,\n              \"Value\": 1,\n              \"Label\": \"Insurance Group:1-8\"\n          },\n          {...}\n          ....\n      ],\n      \"licence-validity-factor\": [\n        {\n            \"Start\": 0,\n            \"End\": 1,\n            \"IsEligible\": true,\n            \"Value\": 1.1,\n            \"Label\": \"Licence Validity:0-1\"\n        },\n        {...}\n        ....\n      ]\n    }\n```\n\n\n## Test coverage data\nFollowing is the entire test coverage data for the project\n```\ngo test ./test/... -p 1 -v\n=== RUN   TestMain\n--- PASS: TestMain (0.00s)\nPASS\nok      pricingengine/test  0.359s\n=== RUN   TestPriceGenerationAppWithActualConfigs\n=== RUN   TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-FailureScenario-1\n=== RUN   TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-FailureScenario-2\n=== RUN   TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-FailureScenario-3\n=== RUN   TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-FailureScenario-4\n=== RUN   TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-FailureScenario-5\n=== RUN   TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-SuccessScenario-1\n=== RUN   TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-SuccessScenario-2\n--- PASS: TestPriceGenerationAppWithActualConfigs (0.02s)\n   --- PASS: TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-FailureScenario-1 (0.02s)\n   --- PASS: TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-FailureScenario-2 (0.00s)\n   --- PASS: TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-FailureScenario-3 (0.00s)\n   --- PASS: TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-FailureScenario-4 (0.00s)\n   --- PASS: TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-FailureScenario-5 (0.00s)\n   --- PASS: TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-SuccessScenario-1 (0.00s)\n   --- PASS: TestPriceGenerationAppWithActualConfigs/TestPriceGenerationAppToGetValidGeneratedPriceList-SuccessScenario-2 (0.00s)\nPASS\nok      pricingengine/test/app  0.414s\n=== RUN   TestConfigCacheScenarios\n=== RUN   TestConfigCacheScenarios/TestConfigCacheInitOnExistingCache\n=== RUN   TestConfigCacheScenarios/TestConfigCacheInitWithNoCacheRefreshButNoTTL\n=== RUN   TestConfigCacheScenarios/TestConfigCacheInitWithCacheRefreshFlag\n=== RUN   TestConfigCacheScenarios/TestConfigCacheInitWithCacheRefreshAfterTimeout\n--- PASS: TestConfigCacheScenarios (0.02s)\n   --- PASS: TestConfigCacheScenarios/TestConfigCacheInitOnExistingCache (0.02s)\n   --- PASS: TestConfigCacheScenarios/TestConfigCacheInitWithNoCacheRefreshButNoTTL (0.00s)\n   --- PASS: TestConfigCacheScenarios/TestConfigCacheInitWithCacheRefreshFlag (0.00s)\n   --- PASS: TestConfigCacheScenarios/TestConfigCacheInitWithCacheRefreshAfterTimeout (0.00s)\n=== RUN   TestConfigFetch\n=== RUN   TestConfigFetch/TestConfigFetchSuccess\n=== RUN   TestConfigFetch/TestConfigFetchFileNotFoundError\n--- PASS: TestConfigFetch (0.00s)\n   --- PASS: TestConfigFetch/TestConfigFetchSuccess (0.00s)\n   --- PASS: TestConfigFetch/TestConfigFetchFileNotFoundError (0.00s)\nPASS\nok      pricingengine/test/config   0.389s\n=== RUN   TestServiceEndpointsIntegrationTestWithActualConfigs\n=== RUN   TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-FailureScenario-1\n[64/64]0xc0000a8200\nWant 'some', got '{\"input\":{\"date_of_birth\":\"\",\"insurance_group\":0,\"license_held_since\":\"\"},\"is-eligible\":false,\"message\":\"DateOfBirth cannot be empty\",\"pricing\":null}'0xc0000927e0\n=== RUN   TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-FailureScenario-2\n[74/80]0xc00010c0f0\nWant 'some', got '{\"input\":{\"date_of_birth\":\"2006-01-02\",\"insurance_group\":0,\"license_held_since\":\"\"},\"is-eligible\":false,\"message\":\"InsuranceGroup should be a Positive number\",\"pricing\":null}'0xc000092c60\n=== RUN   TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-FailureScenario-3\n[75/80]0xc00010c230\nWant 'some', got '{\"input\":{\"date_of_birth\":\"2006-01-02\",\"insurance_group\":20,\"license_held_since\":\"\"},\"is-eligible\":false,\"message\":\"LicenseHeldSince Date cannot be empty\",\"pricing\":null}'0xc0000930e0\n=== RUN   TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-FailureScenario-4\n[85/96]0xc000096720\nWant 'some', got '{\"input\":{\"date_of_birth\":\"2001-01-02\",\"insurance_group\":20,\"license_held_since\":\"2006-01-02\"},\"is-eligible\":false,\"message\":\"Declined due to :Insurance Group:8\",\"pricing\":null}'0xc000093560\n=== RUN   TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-FailureScenario-5\n[85/96]0xc000096900\nWant 'some', got '{\"input\":{\"date_of_birth\":\"2006-08-02\",\"insurance_group\":20,\"license_held_since\":\"2006-01-02\"},\"is-eligible\":false,\"message\":\"Declined due to :Driver Age:0-16\",\"pricing\":null}'0xc0000939e0\n=== RUN   TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-SuccessScenario-1\n[84/96]0xc000096ae0\nWant 'some', got '{\"input\":{\"date_of_birth\":\"2001-08-02\",\"insurance_group\":7,\"license_held_since\":\"2014-08-02\"},\"is-eligible\":true,\"message\":\"Success\",\"pricing\":[{\"premium\":259.349,\"currency\":\"£\",\"fare_group\":\"0.5 hours, Driver Age:16-26, Insurance Group:1-8, Licence Validity:6\"},{\"premium\":4943.8,\"currency\":\"£\",\"fare_group\":\"96 hours / 4 days, Driver Age:16-26, Insurance Group:1-8, Licence Validity:6\"}]}'0xc000093e60\n=== RUN   TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-SuccessScenario-2\n[84/96]0xc000096d80\nWant 'some', got '{\"input\":{\"date_of_birth\":\"2001-08-02\",\"insurance_group\":7,\"license_held_since\":\"2016-08-02\"},\"is-eligible\":true,\"message\":\"Success\",\"pricing\":[{\"premium\":300.3,\"currency\":\"£\",\"fare_group\":\"0.5 hours, Driver Age:16-26, Insurance Group:1-8, Licence Validity:0-6\"},{\"premium\":5724.4,\"currency\":\"£\",\"fare_group\":\"96 hours / 4 days, Driver Age:16-26, Insurance Group:1-8, Licence Validity:0-6\"}]}'0xc000144300\n--- PASS: TestServiceEndpointsIntegrationTestWithActualConfigs (0.03s)\n   --- PASS: TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-FailureScenario-1 (0.02s)\n   --- PASS: TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-FailureScenario-2 (0.00s)\n   --- PASS: TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-FailureScenario-3 (0.00s)\n   --- PASS: TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-FailureScenario-4 (0.00s)\n   --- PASS: TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-FailureScenario-5 (0.00s)\n   --- PASS: TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-SuccessScenario-1 (0.00s)\n   --- PASS: TestServiceEndpointsIntegrationTestWithActualConfigs/TestRESTAPIEndpointToGetValidGeneratedPriceList-SuccessScenario-2 (0.00s)\nPASS\nok      pricingengine/test/service  0.463s\n=== RUN   TestPricingStrategyVariedConfigs\n=== RUN   TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToApplyBaseStrategyWithChainStrategy\n2021/08/02 17:04:23 Found a chain function, Passing on the result for further computation\n=== RUN   TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToApplyBaseStrategyWithoutChainStrategy\n=== RUN   TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToApplySubsecuentFactorsToPricingWithChainStrategy\n2021/08/02 17:04:23 Found a chain function, Passing on the result for further computation\n2021/08/02 17:04:23 Found a chain function, Passing on the result for further computation\n=== RUN   TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToApplySubsecuentFactorsToPricingWithoutChainStrategy\n2021/08/02 17:04:23 Found a chain function, Passing on the result for further computation\n=== RUN   TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingLicenceValidityFactor-Success-Senario\n=== RUN   TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingLicenceValidityFactor-NotFound-Senario\n=== RUN   TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingLicenceValidityFactor-InvalidDate-Senario\n=== RUN   TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingInsuranceGroupFactor-Success-Senario\n=== RUN   TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingInsuranceGroupFactor-NotFound-Senario\n=== RUN   TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingDriverAgeFactor-Success-Senario\n2021/08/02 17:04:23 Checking the driver factor for date_of_birth= 2018-08-02  parse_dob_t= 2018-08-02 00:00:00 +0000 UTC  age= 3\n=== RUN   TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingDriverAgeFactor-NotFound-Senario\n=== RUN   TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingDriverAgeFactor-InvalidDate-Senario\n--- PASS: TestPricingStrategyVariedConfigs (0.02s)\n   --- PASS: TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToApplyBaseStrategyWithChainStrategy (0.02s)\n   --- PASS: TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToApplyBaseStrategyWithoutChainStrategy (0.00s)\n   --- PASS: TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToApplySubsecuentFactorsToPricingWithChainStrategy (0.00s)\n   --- PASS: TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToApplySubsecuentFactorsToPricingWithoutChainStrategy (0.00s)\n   --- PASS: TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingLicenceValidityFactor-Success-Senario (0.00s)\n   --- PASS: TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingLicenceValidityFactor-NotFound-Senario (0.00s)\n   --- PASS: TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingLicenceValidityFactor-InvalidDate-Senario (0.00s)\n   --- PASS: TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingInsuranceGroupFactor-Success-Senario (0.00s)\n   --- PASS: TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingInsuranceGroupFactor-NotFound-Senario (0.00s)\n   --- PASS: TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingDriverAgeFactor-Success-Senario (0.00s)\n   --- PASS: TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingDriverAgeFactor-NotFound-Senario (0.00s)\n   --- PASS: TestPricingStrategyVariedConfigs/TestPricingStrategyVariedConfigsToFindMatchingDriverAgeFactor-InvalidDate-Senario (0.00s)\nPASS\nok      pricingengine/test/strategy 0.383s\n=== RUN   TestAssert\n--- PASS: TestAssert (0.00s)\n=== RUN   TestFactorMapperFeaturesWithMockConfigs\n=== RUN   TestFactorMapperFeaturesWithMockConfigs/TestFactorMapperBaseRateToRangeConfig-SuccessScenario\n=== RUN   TestFactorMapperFeaturesWithMockConfigs/TestFactorMapperDriverAgeFactorToRangeConfig-SuccessScenario\n=== RUN   TestFactorMapperFeaturesWithMockConfigs/TestFactorMapperInsuranceGroupFactorToRangeConfig-SuccessScenario\n=== RUN   TestFactorMapperFeaturesWithMockConfigs/TestFactorMapperLicenceValidityFactorToRangeConfig-SuccessScenario\n--- PASS: TestFactorMapperFeaturesWithMockConfigs (0.00s)\n   --- PASS: TestFactorMapperFeaturesWithMockConfigs/TestFactorMapperBaseRateToRangeConfig-SuccessScenario (0.00s)\n   --- PASS: TestFactorMapperFeaturesWithMockConfigs/TestFactorMapperDriverAgeFactorToRangeConfig-SuccessScenario (0.00s)\n   --- PASS: TestFactorMapperFeaturesWithMockConfigs/TestFactorMapperInsuranceGroupFactorToRangeConfig-SuccessScenario (0.00s)\n   --- PASS: TestFactorMapperFeaturesWithMockConfigs/TestFactorMapperLicenceValidityFactorToRangeConfig-SuccessScenario (0.00s)\nPASS\nok      pricingengine/test/util 0.449s\n➜  pricing-engine-go git:(master)\n```\n\n\n## Enhancements and Potential Improvements:\n- Improving performance using concurrency. I am a fan of multi-threading and concurrency. However, given that the expected time to complete this task is 4-5 hours and I am totally new to the functional programming aspects of GoLang I was not able to wrap my head around few of the concurrency concepts like `defer`, `wait-group` and `go coroutine` execution all together.\n- Better error handling at some of the places can be done to have better user response. Aspects like `fail fast` or `Circuit Breakers` can be applied to quickly break the chained flow and give defined error messages to the end user.\n\n## Time taken to implement the solution:\n- This solution has been completely implemented within *4-5 hours* of time on top of *+2 hours* to understand the basics of the `GoLang` syntaxes and other programming nuances.\n\n\n## Learning\n- I have mostly worked on OO programming languages like Java and C++, working on a pure functional language like GoLang is really a fun challenge. I learned a lot from the functional perspective and some minute data handling and code structuring that enabled proper control of data flow.\n- Generalisation and Specialisation of certain aspects like ConfigFetcher and FactoMapper was really interesting learning as well. Applying delegated single responsibility helped a lot with a granular scoped testing and also helped in plugging in and toggle for tests such that the behaviour can be changed at different layers according, kind of similar to DependencyInjection and InversionOfControl aspects in pure Object Oriented languages\n- It was really exciting to use OO patterns in a functional programming language like Go. The code looked really clean on brining in the Chain of Commands approach in combination with Strategy pattern that typically revolved around the Function as a datatype concept.\n\n## Setting up the environment\nThis application is purely build on goLang. To run this application in your local you need to follow the below steps\n\n#### Build:\n```\ngo build ./cmd/.\n```\n\n#### Run\n```\ngo run ./cmd/.\n```\n\n\n#### Test\n```\ngo test ./test/... -p 1\n```\n\n\n### References:\nSince this is a beginner/learning project based on GoLang I had to go through a lot of documentations and references related to syntaxes and approaches in arriving at the actual problem solution. All the references that were really useful during the project working can be found here: [References](./REFERENCES.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frajagopal28%2Fpricing-engine-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frajagopal28%2Fpricing-engine-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frajagopal28%2Fpricing-engine-go/lists"}