Mini eCommerce application and message/warning/error-handlingAsync and error-handlingGeneric error message...

Can a Gentile theist be saved?

Stereotypical names

Can I use my Chinese passport to enter China after I acquired another citizenship?

What will be the benefits of Brexit?

Simple recursive Sudoku solver

Superhero words!

Science Fiction story where a man invents a machine that can help him watch history unfold

Java - What do constructor type arguments mean when placed *before* the type?

I'm in charge of equipment buying but no one's ever happy with what I choose. How to fix this?

What is the opposite of 'gravitas'?

Should my PhD thesis be submitted under my legal name?

How to prevent YouTube from showing already watched videos?

Is there enough fresh water in the world to eradicate the drinking water crisis?

Should a half Jewish man be discouraged from marrying a Jewess?

Is there an Impartial Brexit Deal comparison site?

Identify a stage play about a VR experience in which participants are encouraged to simulate performing horrific activities

Teaching indefinite integrals that require special-casing

Why does this part of the Space Shuttle launch pad seem to be floating in air?

Lifted its hind leg on or lifted its hind leg towards?

Did US corporations pay demonstrators in the German demonstrations against article 13?

Adding empty element to declared container without declaring type of element

What if somebody invests in my application?

What do you call the infoboxes with text and sometimes images on the side of a page we find in textbooks?

Giant Toughroad SLR 2 for 200 miles in two days, will it make it?



Mini eCommerce application and message/warning/error-handling


Async and error-handlingGeneric error message factoryError-handling / Self-Validating mechanismError handling for Stripe credit card processingRead sales transactions from ExcelValidating each process object from its own validatorAdding various types of configurable validators to fieldsHello World Node application with error handlingValidation for a CQS system that throws an exceptionApproach for validation in domain layer













4












$begingroup$


I decided to build a basic eCommerce application in c++ as a learning experience. In the past, I've gone with a fat controller, skinny model approach, and while it has worked, my results always end up messy and the code is/was kind of individual to the applications. To try something new, I wanted to incorporate the single responsibility principle (as much as possible) and group my application into service layers.



My current code can be found on Github.



Continuing with the actual architecture:



Entities: Base data-holder class.



Services: Handle all operations for a particular entity. Building, creating, updating, finding, and more.



Validators: Check entity data to ensure it meets any requirements. Interface is currently a single method, Validate(void * v). Implementation casts the passed in pointer to the appropriate entity.



int main(int argc, char ** argv)
{
Pluto::Controller::AddSingleProduct * controllerAddSingleProduct = new Pluto::Controller::AddSingleProduct();
controllerAddSingleProduct->ProcessRequest();

return 0;
}


The ProcessRequest method called above:



void Pluto::Controller::AddSingleProduct::ProcessRequest()
{
unsigned int productId = 1;
std::string productName = "Test product.";
unsigned int productPrice = 10;
unsigned int productQuantity = 10;
unsigned int productType = 1;
unsigned int productStatus = 1;

Pluto::Service::Product * productService = new Pluto::Service::Product();
productService->SetProductValidator(new Pluto::Validator::Product());

if (productService->Create(productService->Build(productId, productName, productPrice, productQuantity, productType, productStatus)))
{
this->ErrorView();
return;
}

this->SuccessView();
}


The Pluto::Service::Product methods called above:



Pluto::Entity::Product * Pluto::Service::Product::Build(unsigned int id, std::string name, unsigned int price, unsigned int quantity, unsigned int type, unsigned int status)
{
Pluto::Entity::Product * p = new Pluto::Entity::Product();
p->SetId(id);
p->SetName(name);
p->SetPrice(price);
p->SetQuantity(quantity);
p->SetType(type);
p->SetStatus(status);

return p;
}

unsigned int Pluto::Service::Product::Create(Pluto::Entity::Product * product)
{
if (product == NULL)
{
return 1;
}

if (this->productValidator)
{
if (this->productValidator->Validate(product))
{
return 1;
}
}

return 0;
}

void Pluto::Service::Product::SetProductValidator(Pluto::Validator::Product * productValidator)
{
this->productValidator = productValidator;
}


And finally, the Pluto::Validator::Product methods:



unsigned int Pluto::Validator::Product::Validate(void * v)
{
Pluto::Entity::Product * p = (Pluto::Entity::Product *) v;

if (p == NULL)
{
this->messageContainer->AddMessage
(
Pluto::Core::Message::TYPE_ERROR,
Pluto::Validator::Product::ERROR_DEFAULT,
"Invalid product.",
(void *) p
);

return 1;
}

if (p->GetId() < 1)
{
this->messageContainer->AddMessage
(
Pluto::Core::Message::TYPE_ERROR,
Pluto::Validator::Product::ERROR_ID,
"Invalid ID.",
(void *) p
);
}

if (p->GetName().length() < 3)
{
this->messageContainer->AddMessage
(
Pluto::Core::Message::TYPE_ERROR,
Pluto::Validator::Product::ERROR_NAME,
"Invalid name.",
(void *) p
);
}

if (p->GetPrice() < 1)
{
this->messageContainer->AddMessage
(
Pluto::Core::Message::TYPE_ERROR,
Pluto::Validator::Product::ERROR_PRICE,
"Invalid price.",
(void *) p
);
}

if (p->GetQuantity() < 1)
{
this->messageContainer->AddMessage
(
Pluto::Core::Message::TYPE_ERROR,
Pluto::Validator::Product::ERROR_QUANTITY,
"Invalid quantity.",
(void *) p
);
}

if (p->GetType() < 0)
{
this->messageContainer->AddMessage
(
Pluto::Core::Message::TYPE_ERROR,
Pluto::Validator::Product::ERROR_TYPE,
"Invalid type.",
(void *) p
);
}

if (p->GetStatus() < 0)
{
this->messageContainer->AddMessage
(
Pluto::Core::Message::TYPE_ERROR,
Pluto::Validator::Product::ERROR_STATUS,
"Invalid status.",
(void *) p
);
}

if (this->messageContainer->GetNumMessages())
{
return 1;
}

return 0;
}


The messageContainer within Pluto::Validator::Product, and all validators, for that matter is a very basic array-like class. Calling AddMessage places the data passed in at the current position and then increments. The details of it can be found in the repository linked above.



I feel my controllers are a lot more maintainable and manageable like this. The product service seems flexible in that I can switch the validator as necessary to test or deal with different validation rules by subclassing. I'm also comfortable with how the validator class worked out. It's really basic, but it seems isolated and only handles its one job.



The problem I'm running into is, how do I get messages/warnings/errors/etc back out to the controller efficiently? The message container class seen above was an attempt to solve this problem. It works great if I instantiate the validator in the controller and use it, then utilize the message container it provides in the views. I'm having trouble seeing a good way to do this in the product service, and then passing the data back out to the controller.



Below is a quick example of how I might have done validation before:



void AddProductController()
{
Pluto::Entity::Product * p = new Pluto::Entity::Product();
p->SetId(1);
p->SetName("Example Product");
// Continue populating product as before.

unsigned int validationErrors = 0;
unsigned int maxValidationErrors = 5;
unsigned int numValidationErrors = 0;
std::string errorStrings[maxValidationErrors];

if (p->GetId() < 1)
{
validationErrors = 1;
errorStrings[numValidationErrors] = "The product ID was invalid.";
numValidationErrors++;
}

if (p->GetName().length() < 3)
{
validationErrors = 1;
errorStrings[numValidationErrors] = "The product name was too short.";
numValidationErrors++;
}

// Continue checking the product data.

if (validationErrors)
{
for (unsigned int i = 0; i < numValidationErrors; i++)
{
std::cout << "Error: " << errorStrings[i] << "n";
}

return;
}

std::cout << "Product was validated successfully!n";
}


Worth noting, the service might have other things it does as well that need to spit back messages or errors to the client. For example, persisting the product on disk somehow, which could succeed or fail for any number of reasons. In the future, I may want to expand my views to use HTML or some type of layout as well, so it seems I'd need to be able to detect certain kinds of messages I'm expecting for positioning.



Am I correct in thinking that my approach to the services and validators is worthwhile? Any suggestions on how to handle the message/warning/error issue discussed above is really appreciated. It's something that's holding me back right now.










share|improve this question











$endgroup$

















    4












    $begingroup$


    I decided to build a basic eCommerce application in c++ as a learning experience. In the past, I've gone with a fat controller, skinny model approach, and while it has worked, my results always end up messy and the code is/was kind of individual to the applications. To try something new, I wanted to incorporate the single responsibility principle (as much as possible) and group my application into service layers.



    My current code can be found on Github.



    Continuing with the actual architecture:



    Entities: Base data-holder class.



    Services: Handle all operations for a particular entity. Building, creating, updating, finding, and more.



    Validators: Check entity data to ensure it meets any requirements. Interface is currently a single method, Validate(void * v). Implementation casts the passed in pointer to the appropriate entity.



    int main(int argc, char ** argv)
    {
    Pluto::Controller::AddSingleProduct * controllerAddSingleProduct = new Pluto::Controller::AddSingleProduct();
    controllerAddSingleProduct->ProcessRequest();

    return 0;
    }


    The ProcessRequest method called above:



    void Pluto::Controller::AddSingleProduct::ProcessRequest()
    {
    unsigned int productId = 1;
    std::string productName = "Test product.";
    unsigned int productPrice = 10;
    unsigned int productQuantity = 10;
    unsigned int productType = 1;
    unsigned int productStatus = 1;

    Pluto::Service::Product * productService = new Pluto::Service::Product();
    productService->SetProductValidator(new Pluto::Validator::Product());

    if (productService->Create(productService->Build(productId, productName, productPrice, productQuantity, productType, productStatus)))
    {
    this->ErrorView();
    return;
    }

    this->SuccessView();
    }


    The Pluto::Service::Product methods called above:



    Pluto::Entity::Product * Pluto::Service::Product::Build(unsigned int id, std::string name, unsigned int price, unsigned int quantity, unsigned int type, unsigned int status)
    {
    Pluto::Entity::Product * p = new Pluto::Entity::Product();
    p->SetId(id);
    p->SetName(name);
    p->SetPrice(price);
    p->SetQuantity(quantity);
    p->SetType(type);
    p->SetStatus(status);

    return p;
    }

    unsigned int Pluto::Service::Product::Create(Pluto::Entity::Product * product)
    {
    if (product == NULL)
    {
    return 1;
    }

    if (this->productValidator)
    {
    if (this->productValidator->Validate(product))
    {
    return 1;
    }
    }

    return 0;
    }

    void Pluto::Service::Product::SetProductValidator(Pluto::Validator::Product * productValidator)
    {
    this->productValidator = productValidator;
    }


    And finally, the Pluto::Validator::Product methods:



    unsigned int Pluto::Validator::Product::Validate(void * v)
    {
    Pluto::Entity::Product * p = (Pluto::Entity::Product *) v;

    if (p == NULL)
    {
    this->messageContainer->AddMessage
    (
    Pluto::Core::Message::TYPE_ERROR,
    Pluto::Validator::Product::ERROR_DEFAULT,
    "Invalid product.",
    (void *) p
    );

    return 1;
    }

    if (p->GetId() < 1)
    {
    this->messageContainer->AddMessage
    (
    Pluto::Core::Message::TYPE_ERROR,
    Pluto::Validator::Product::ERROR_ID,
    "Invalid ID.",
    (void *) p
    );
    }

    if (p->GetName().length() < 3)
    {
    this->messageContainer->AddMessage
    (
    Pluto::Core::Message::TYPE_ERROR,
    Pluto::Validator::Product::ERROR_NAME,
    "Invalid name.",
    (void *) p
    );
    }

    if (p->GetPrice() < 1)
    {
    this->messageContainer->AddMessage
    (
    Pluto::Core::Message::TYPE_ERROR,
    Pluto::Validator::Product::ERROR_PRICE,
    "Invalid price.",
    (void *) p
    );
    }

    if (p->GetQuantity() < 1)
    {
    this->messageContainer->AddMessage
    (
    Pluto::Core::Message::TYPE_ERROR,
    Pluto::Validator::Product::ERROR_QUANTITY,
    "Invalid quantity.",
    (void *) p
    );
    }

    if (p->GetType() < 0)
    {
    this->messageContainer->AddMessage
    (
    Pluto::Core::Message::TYPE_ERROR,
    Pluto::Validator::Product::ERROR_TYPE,
    "Invalid type.",
    (void *) p
    );
    }

    if (p->GetStatus() < 0)
    {
    this->messageContainer->AddMessage
    (
    Pluto::Core::Message::TYPE_ERROR,
    Pluto::Validator::Product::ERROR_STATUS,
    "Invalid status.",
    (void *) p
    );
    }

    if (this->messageContainer->GetNumMessages())
    {
    return 1;
    }

    return 0;
    }


    The messageContainer within Pluto::Validator::Product, and all validators, for that matter is a very basic array-like class. Calling AddMessage places the data passed in at the current position and then increments. The details of it can be found in the repository linked above.



    I feel my controllers are a lot more maintainable and manageable like this. The product service seems flexible in that I can switch the validator as necessary to test or deal with different validation rules by subclassing. I'm also comfortable with how the validator class worked out. It's really basic, but it seems isolated and only handles its one job.



    The problem I'm running into is, how do I get messages/warnings/errors/etc back out to the controller efficiently? The message container class seen above was an attempt to solve this problem. It works great if I instantiate the validator in the controller and use it, then utilize the message container it provides in the views. I'm having trouble seeing a good way to do this in the product service, and then passing the data back out to the controller.



    Below is a quick example of how I might have done validation before:



    void AddProductController()
    {
    Pluto::Entity::Product * p = new Pluto::Entity::Product();
    p->SetId(1);
    p->SetName("Example Product");
    // Continue populating product as before.

    unsigned int validationErrors = 0;
    unsigned int maxValidationErrors = 5;
    unsigned int numValidationErrors = 0;
    std::string errorStrings[maxValidationErrors];

    if (p->GetId() < 1)
    {
    validationErrors = 1;
    errorStrings[numValidationErrors] = "The product ID was invalid.";
    numValidationErrors++;
    }

    if (p->GetName().length() < 3)
    {
    validationErrors = 1;
    errorStrings[numValidationErrors] = "The product name was too short.";
    numValidationErrors++;
    }

    // Continue checking the product data.

    if (validationErrors)
    {
    for (unsigned int i = 0; i < numValidationErrors; i++)
    {
    std::cout << "Error: " << errorStrings[i] << "n";
    }

    return;
    }

    std::cout << "Product was validated successfully!n";
    }


    Worth noting, the service might have other things it does as well that need to spit back messages or errors to the client. For example, persisting the product on disk somehow, which could succeed or fail for any number of reasons. In the future, I may want to expand my views to use HTML or some type of layout as well, so it seems I'd need to be able to detect certain kinds of messages I'm expecting for positioning.



    Am I correct in thinking that my approach to the services and validators is worthwhile? Any suggestions on how to handle the message/warning/error issue discussed above is really appreciated. It's something that's holding me back right now.










    share|improve this question











    $endgroup$















      4












      4








      4





      $begingroup$


      I decided to build a basic eCommerce application in c++ as a learning experience. In the past, I've gone with a fat controller, skinny model approach, and while it has worked, my results always end up messy and the code is/was kind of individual to the applications. To try something new, I wanted to incorporate the single responsibility principle (as much as possible) and group my application into service layers.



      My current code can be found on Github.



      Continuing with the actual architecture:



      Entities: Base data-holder class.



      Services: Handle all operations for a particular entity. Building, creating, updating, finding, and more.



      Validators: Check entity data to ensure it meets any requirements. Interface is currently a single method, Validate(void * v). Implementation casts the passed in pointer to the appropriate entity.



      int main(int argc, char ** argv)
      {
      Pluto::Controller::AddSingleProduct * controllerAddSingleProduct = new Pluto::Controller::AddSingleProduct();
      controllerAddSingleProduct->ProcessRequest();

      return 0;
      }


      The ProcessRequest method called above:



      void Pluto::Controller::AddSingleProduct::ProcessRequest()
      {
      unsigned int productId = 1;
      std::string productName = "Test product.";
      unsigned int productPrice = 10;
      unsigned int productQuantity = 10;
      unsigned int productType = 1;
      unsigned int productStatus = 1;

      Pluto::Service::Product * productService = new Pluto::Service::Product();
      productService->SetProductValidator(new Pluto::Validator::Product());

      if (productService->Create(productService->Build(productId, productName, productPrice, productQuantity, productType, productStatus)))
      {
      this->ErrorView();
      return;
      }

      this->SuccessView();
      }


      The Pluto::Service::Product methods called above:



      Pluto::Entity::Product * Pluto::Service::Product::Build(unsigned int id, std::string name, unsigned int price, unsigned int quantity, unsigned int type, unsigned int status)
      {
      Pluto::Entity::Product * p = new Pluto::Entity::Product();
      p->SetId(id);
      p->SetName(name);
      p->SetPrice(price);
      p->SetQuantity(quantity);
      p->SetType(type);
      p->SetStatus(status);

      return p;
      }

      unsigned int Pluto::Service::Product::Create(Pluto::Entity::Product * product)
      {
      if (product == NULL)
      {
      return 1;
      }

      if (this->productValidator)
      {
      if (this->productValidator->Validate(product))
      {
      return 1;
      }
      }

      return 0;
      }

      void Pluto::Service::Product::SetProductValidator(Pluto::Validator::Product * productValidator)
      {
      this->productValidator = productValidator;
      }


      And finally, the Pluto::Validator::Product methods:



      unsigned int Pluto::Validator::Product::Validate(void * v)
      {
      Pluto::Entity::Product * p = (Pluto::Entity::Product *) v;

      if (p == NULL)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_DEFAULT,
      "Invalid product.",
      (void *) p
      );

      return 1;
      }

      if (p->GetId() < 1)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_ID,
      "Invalid ID.",
      (void *) p
      );
      }

      if (p->GetName().length() < 3)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_NAME,
      "Invalid name.",
      (void *) p
      );
      }

      if (p->GetPrice() < 1)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_PRICE,
      "Invalid price.",
      (void *) p
      );
      }

      if (p->GetQuantity() < 1)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_QUANTITY,
      "Invalid quantity.",
      (void *) p
      );
      }

      if (p->GetType() < 0)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_TYPE,
      "Invalid type.",
      (void *) p
      );
      }

      if (p->GetStatus() < 0)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_STATUS,
      "Invalid status.",
      (void *) p
      );
      }

      if (this->messageContainer->GetNumMessages())
      {
      return 1;
      }

      return 0;
      }


      The messageContainer within Pluto::Validator::Product, and all validators, for that matter is a very basic array-like class. Calling AddMessage places the data passed in at the current position and then increments. The details of it can be found in the repository linked above.



      I feel my controllers are a lot more maintainable and manageable like this. The product service seems flexible in that I can switch the validator as necessary to test or deal with different validation rules by subclassing. I'm also comfortable with how the validator class worked out. It's really basic, but it seems isolated and only handles its one job.



      The problem I'm running into is, how do I get messages/warnings/errors/etc back out to the controller efficiently? The message container class seen above was an attempt to solve this problem. It works great if I instantiate the validator in the controller and use it, then utilize the message container it provides in the views. I'm having trouble seeing a good way to do this in the product service, and then passing the data back out to the controller.



      Below is a quick example of how I might have done validation before:



      void AddProductController()
      {
      Pluto::Entity::Product * p = new Pluto::Entity::Product();
      p->SetId(1);
      p->SetName("Example Product");
      // Continue populating product as before.

      unsigned int validationErrors = 0;
      unsigned int maxValidationErrors = 5;
      unsigned int numValidationErrors = 0;
      std::string errorStrings[maxValidationErrors];

      if (p->GetId() < 1)
      {
      validationErrors = 1;
      errorStrings[numValidationErrors] = "The product ID was invalid.";
      numValidationErrors++;
      }

      if (p->GetName().length() < 3)
      {
      validationErrors = 1;
      errorStrings[numValidationErrors] = "The product name was too short.";
      numValidationErrors++;
      }

      // Continue checking the product data.

      if (validationErrors)
      {
      for (unsigned int i = 0; i < numValidationErrors; i++)
      {
      std::cout << "Error: " << errorStrings[i] << "n";
      }

      return;
      }

      std::cout << "Product was validated successfully!n";
      }


      Worth noting, the service might have other things it does as well that need to spit back messages or errors to the client. For example, persisting the product on disk somehow, which could succeed or fail for any number of reasons. In the future, I may want to expand my views to use HTML or some type of layout as well, so it seems I'd need to be able to detect certain kinds of messages I'm expecting for positioning.



      Am I correct in thinking that my approach to the services and validators is worthwhile? Any suggestions on how to handle the message/warning/error issue discussed above is really appreciated. It's something that's holding me back right now.










      share|improve this question











      $endgroup$




      I decided to build a basic eCommerce application in c++ as a learning experience. In the past, I've gone with a fat controller, skinny model approach, and while it has worked, my results always end up messy and the code is/was kind of individual to the applications. To try something new, I wanted to incorporate the single responsibility principle (as much as possible) and group my application into service layers.



      My current code can be found on Github.



      Continuing with the actual architecture:



      Entities: Base data-holder class.



      Services: Handle all operations for a particular entity. Building, creating, updating, finding, and more.



      Validators: Check entity data to ensure it meets any requirements. Interface is currently a single method, Validate(void * v). Implementation casts the passed in pointer to the appropriate entity.



      int main(int argc, char ** argv)
      {
      Pluto::Controller::AddSingleProduct * controllerAddSingleProduct = new Pluto::Controller::AddSingleProduct();
      controllerAddSingleProduct->ProcessRequest();

      return 0;
      }


      The ProcessRequest method called above:



      void Pluto::Controller::AddSingleProduct::ProcessRequest()
      {
      unsigned int productId = 1;
      std::string productName = "Test product.";
      unsigned int productPrice = 10;
      unsigned int productQuantity = 10;
      unsigned int productType = 1;
      unsigned int productStatus = 1;

      Pluto::Service::Product * productService = new Pluto::Service::Product();
      productService->SetProductValidator(new Pluto::Validator::Product());

      if (productService->Create(productService->Build(productId, productName, productPrice, productQuantity, productType, productStatus)))
      {
      this->ErrorView();
      return;
      }

      this->SuccessView();
      }


      The Pluto::Service::Product methods called above:



      Pluto::Entity::Product * Pluto::Service::Product::Build(unsigned int id, std::string name, unsigned int price, unsigned int quantity, unsigned int type, unsigned int status)
      {
      Pluto::Entity::Product * p = new Pluto::Entity::Product();
      p->SetId(id);
      p->SetName(name);
      p->SetPrice(price);
      p->SetQuantity(quantity);
      p->SetType(type);
      p->SetStatus(status);

      return p;
      }

      unsigned int Pluto::Service::Product::Create(Pluto::Entity::Product * product)
      {
      if (product == NULL)
      {
      return 1;
      }

      if (this->productValidator)
      {
      if (this->productValidator->Validate(product))
      {
      return 1;
      }
      }

      return 0;
      }

      void Pluto::Service::Product::SetProductValidator(Pluto::Validator::Product * productValidator)
      {
      this->productValidator = productValidator;
      }


      And finally, the Pluto::Validator::Product methods:



      unsigned int Pluto::Validator::Product::Validate(void * v)
      {
      Pluto::Entity::Product * p = (Pluto::Entity::Product *) v;

      if (p == NULL)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_DEFAULT,
      "Invalid product.",
      (void *) p
      );

      return 1;
      }

      if (p->GetId() < 1)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_ID,
      "Invalid ID.",
      (void *) p
      );
      }

      if (p->GetName().length() < 3)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_NAME,
      "Invalid name.",
      (void *) p
      );
      }

      if (p->GetPrice() < 1)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_PRICE,
      "Invalid price.",
      (void *) p
      );
      }

      if (p->GetQuantity() < 1)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_QUANTITY,
      "Invalid quantity.",
      (void *) p
      );
      }

      if (p->GetType() < 0)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_TYPE,
      "Invalid type.",
      (void *) p
      );
      }

      if (p->GetStatus() < 0)
      {
      this->messageContainer->AddMessage
      (
      Pluto::Core::Message::TYPE_ERROR,
      Pluto::Validator::Product::ERROR_STATUS,
      "Invalid status.",
      (void *) p
      );
      }

      if (this->messageContainer->GetNumMessages())
      {
      return 1;
      }

      return 0;
      }


      The messageContainer within Pluto::Validator::Product, and all validators, for that matter is a very basic array-like class. Calling AddMessage places the data passed in at the current position and then increments. The details of it can be found in the repository linked above.



      I feel my controllers are a lot more maintainable and manageable like this. The product service seems flexible in that I can switch the validator as necessary to test or deal with different validation rules by subclassing. I'm also comfortable with how the validator class worked out. It's really basic, but it seems isolated and only handles its one job.



      The problem I'm running into is, how do I get messages/warnings/errors/etc back out to the controller efficiently? The message container class seen above was an attempt to solve this problem. It works great if I instantiate the validator in the controller and use it, then utilize the message container it provides in the views. I'm having trouble seeing a good way to do this in the product service, and then passing the data back out to the controller.



      Below is a quick example of how I might have done validation before:



      void AddProductController()
      {
      Pluto::Entity::Product * p = new Pluto::Entity::Product();
      p->SetId(1);
      p->SetName("Example Product");
      // Continue populating product as before.

      unsigned int validationErrors = 0;
      unsigned int maxValidationErrors = 5;
      unsigned int numValidationErrors = 0;
      std::string errorStrings[maxValidationErrors];

      if (p->GetId() < 1)
      {
      validationErrors = 1;
      errorStrings[numValidationErrors] = "The product ID was invalid.";
      numValidationErrors++;
      }

      if (p->GetName().length() < 3)
      {
      validationErrors = 1;
      errorStrings[numValidationErrors] = "The product name was too short.";
      numValidationErrors++;
      }

      // Continue checking the product data.

      if (validationErrors)
      {
      for (unsigned int i = 0; i < numValidationErrors; i++)
      {
      std::cout << "Error: " << errorStrings[i] << "n";
      }

      return;
      }

      std::cout << "Product was validated successfully!n";
      }


      Worth noting, the service might have other things it does as well that need to spit back messages or errors to the client. For example, persisting the product on disk somehow, which could succeed or fail for any number of reasons. In the future, I may want to expand my views to use HTML or some type of layout as well, so it seems I'd need to be able to detect certain kinds of messages I'm expecting for positioning.



      Am I correct in thinking that my approach to the services and validators is worthwhile? Any suggestions on how to handle the message/warning/error issue discussed above is really appreciated. It's something that's holding me back right now.







      c++ validation mvc error-handling e-commerce






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Jan 3 '16 at 2:57









      Jamal

      30.4k11121227




      30.4k11121227










      asked Aug 31 '14 at 8:26









      ChickenFarmerChickenFarmer

      292




      292






















          1 Answer
          1






          active

          oldest

          votes


















          3





          +50







          $begingroup$

          I would agree that what you have now is cleaner and more testable (in comparison to the code you said you might have written before).



          I don't know C++ but I'm familiar with validation, so I'll stick to that.




          • When adding a Product, I think it's best not to explicity set an ID. I'm assuming these entries are going to a database of some sorts (relational, key, or anything else). If this is the case, let the database set the ID, or let it tell you what ID comes up next.


          • I think it's limiting to have restrictions on name length. I could see an eCommerce store selling an ax. I'm not sure why you'd limit one and two letter words (why not three or four letter words);just check if there's a name at all.


          • Similar principle with the price. Supposedly it can't be less than 1. What if the store offers a holiday sale and the $1 item is 50% off, and the $0.25 is free! I'm just thinking of all the possible ways you could run into problems down the road.


          • Also, you set price as an int. This is fine, except now there's no cents. I suggest another variable handling the cents (also an int, limited to two digits).







          share|improve this answer









          $endgroup$













          • $begingroup$
            Alex, thanks for the response. It has been quite some time since I wrote this code and have improved a number of things since then. The ID is set explicitly on creation just for initialization sake. It isn't used and is automatically set by the mapping layer. The name length restriction is a minimum and could be changed, i.e. it's a placeholder. I'd handle discounts elsewhere, as that is more of a per-application concept. Price should definitely be an integer. Instead of storing 1.00, 100 is stored, so the cent information is maintained. Formatting the price is view-related.
            $endgroup$
            – ChickenFarmer
            Jan 10 '16 at 17:21












          • $begingroup$
            Maybe my post was of no use, and if I could, I'd return the rep I received from the bounty (which I'd be fine with, I'd rather have my post be helpful than get the rep.) I can't believe I missed the integer for price thing. That's the way to go and I use that method myself anyways.
            $endgroup$
            – Alex L
            Jan 12 '16 at 4:42











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f61607%2fmini-ecommerce-application-and-message-warning-error-handling%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          3





          +50







          $begingroup$

          I would agree that what you have now is cleaner and more testable (in comparison to the code you said you might have written before).



          I don't know C++ but I'm familiar with validation, so I'll stick to that.




          • When adding a Product, I think it's best not to explicity set an ID. I'm assuming these entries are going to a database of some sorts (relational, key, or anything else). If this is the case, let the database set the ID, or let it tell you what ID comes up next.


          • I think it's limiting to have restrictions on name length. I could see an eCommerce store selling an ax. I'm not sure why you'd limit one and two letter words (why not three or four letter words);just check if there's a name at all.


          • Similar principle with the price. Supposedly it can't be less than 1. What if the store offers a holiday sale and the $1 item is 50% off, and the $0.25 is free! I'm just thinking of all the possible ways you could run into problems down the road.


          • Also, you set price as an int. This is fine, except now there's no cents. I suggest another variable handling the cents (also an int, limited to two digits).







          share|improve this answer









          $endgroup$













          • $begingroup$
            Alex, thanks for the response. It has been quite some time since I wrote this code and have improved a number of things since then. The ID is set explicitly on creation just for initialization sake. It isn't used and is automatically set by the mapping layer. The name length restriction is a minimum and could be changed, i.e. it's a placeholder. I'd handle discounts elsewhere, as that is more of a per-application concept. Price should definitely be an integer. Instead of storing 1.00, 100 is stored, so the cent information is maintained. Formatting the price is view-related.
            $endgroup$
            – ChickenFarmer
            Jan 10 '16 at 17:21












          • $begingroup$
            Maybe my post was of no use, and if I could, I'd return the rep I received from the bounty (which I'd be fine with, I'd rather have my post be helpful than get the rep.) I can't believe I missed the integer for price thing. That's the way to go and I use that method myself anyways.
            $endgroup$
            – Alex L
            Jan 12 '16 at 4:42
















          3





          +50







          $begingroup$

          I would agree that what you have now is cleaner and more testable (in comparison to the code you said you might have written before).



          I don't know C++ but I'm familiar with validation, so I'll stick to that.




          • When adding a Product, I think it's best not to explicity set an ID. I'm assuming these entries are going to a database of some sorts (relational, key, or anything else). If this is the case, let the database set the ID, or let it tell you what ID comes up next.


          • I think it's limiting to have restrictions on name length. I could see an eCommerce store selling an ax. I'm not sure why you'd limit one and two letter words (why not three or four letter words);just check if there's a name at all.


          • Similar principle with the price. Supposedly it can't be less than 1. What if the store offers a holiday sale and the $1 item is 50% off, and the $0.25 is free! I'm just thinking of all the possible ways you could run into problems down the road.


          • Also, you set price as an int. This is fine, except now there's no cents. I suggest another variable handling the cents (also an int, limited to two digits).







          share|improve this answer









          $endgroup$













          • $begingroup$
            Alex, thanks for the response. It has been quite some time since I wrote this code and have improved a number of things since then. The ID is set explicitly on creation just for initialization sake. It isn't used and is automatically set by the mapping layer. The name length restriction is a minimum and could be changed, i.e. it's a placeholder. I'd handle discounts elsewhere, as that is more of a per-application concept. Price should definitely be an integer. Instead of storing 1.00, 100 is stored, so the cent information is maintained. Formatting the price is view-related.
            $endgroup$
            – ChickenFarmer
            Jan 10 '16 at 17:21












          • $begingroup$
            Maybe my post was of no use, and if I could, I'd return the rep I received from the bounty (which I'd be fine with, I'd rather have my post be helpful than get the rep.) I can't believe I missed the integer for price thing. That's the way to go and I use that method myself anyways.
            $endgroup$
            – Alex L
            Jan 12 '16 at 4:42














          3





          +50







          3





          +50



          3




          +50



          $begingroup$

          I would agree that what you have now is cleaner and more testable (in comparison to the code you said you might have written before).



          I don't know C++ but I'm familiar with validation, so I'll stick to that.




          • When adding a Product, I think it's best not to explicity set an ID. I'm assuming these entries are going to a database of some sorts (relational, key, or anything else). If this is the case, let the database set the ID, or let it tell you what ID comes up next.


          • I think it's limiting to have restrictions on name length. I could see an eCommerce store selling an ax. I'm not sure why you'd limit one and two letter words (why not three or four letter words);just check if there's a name at all.


          • Similar principle with the price. Supposedly it can't be less than 1. What if the store offers a holiday sale and the $1 item is 50% off, and the $0.25 is free! I'm just thinking of all the possible ways you could run into problems down the road.


          • Also, you set price as an int. This is fine, except now there's no cents. I suggest another variable handling the cents (also an int, limited to two digits).







          share|improve this answer









          $endgroup$



          I would agree that what you have now is cleaner and more testable (in comparison to the code you said you might have written before).



          I don't know C++ but I'm familiar with validation, so I'll stick to that.




          • When adding a Product, I think it's best not to explicity set an ID. I'm assuming these entries are going to a database of some sorts (relational, key, or anything else). If this is the case, let the database set the ID, or let it tell you what ID comes up next.


          • I think it's limiting to have restrictions on name length. I could see an eCommerce store selling an ax. I'm not sure why you'd limit one and two letter words (why not three or four letter words);just check if there's a name at all.


          • Similar principle with the price. Supposedly it can't be less than 1. What if the store offers a holiday sale and the $1 item is 50% off, and the $0.25 is free! I'm just thinking of all the possible ways you could run into problems down the road.


          • Also, you set price as an int. This is fine, except now there's no cents. I suggest another variable handling the cents (also an int, limited to two digits).








          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Jan 3 '16 at 18:04









          Alex LAlex L

          5,18122068




          5,18122068












          • $begingroup$
            Alex, thanks for the response. It has been quite some time since I wrote this code and have improved a number of things since then. The ID is set explicitly on creation just for initialization sake. It isn't used and is automatically set by the mapping layer. The name length restriction is a minimum and could be changed, i.e. it's a placeholder. I'd handle discounts elsewhere, as that is more of a per-application concept. Price should definitely be an integer. Instead of storing 1.00, 100 is stored, so the cent information is maintained. Formatting the price is view-related.
            $endgroup$
            – ChickenFarmer
            Jan 10 '16 at 17:21












          • $begingroup$
            Maybe my post was of no use, and if I could, I'd return the rep I received from the bounty (which I'd be fine with, I'd rather have my post be helpful than get the rep.) I can't believe I missed the integer for price thing. That's the way to go and I use that method myself anyways.
            $endgroup$
            – Alex L
            Jan 12 '16 at 4:42


















          • $begingroup$
            Alex, thanks for the response. It has been quite some time since I wrote this code and have improved a number of things since then. The ID is set explicitly on creation just for initialization sake. It isn't used and is automatically set by the mapping layer. The name length restriction is a minimum and could be changed, i.e. it's a placeholder. I'd handle discounts elsewhere, as that is more of a per-application concept. Price should definitely be an integer. Instead of storing 1.00, 100 is stored, so the cent information is maintained. Formatting the price is view-related.
            $endgroup$
            – ChickenFarmer
            Jan 10 '16 at 17:21












          • $begingroup$
            Maybe my post was of no use, and if I could, I'd return the rep I received from the bounty (which I'd be fine with, I'd rather have my post be helpful than get the rep.) I can't believe I missed the integer for price thing. That's the way to go and I use that method myself anyways.
            $endgroup$
            – Alex L
            Jan 12 '16 at 4:42
















          $begingroup$
          Alex, thanks for the response. It has been quite some time since I wrote this code and have improved a number of things since then. The ID is set explicitly on creation just for initialization sake. It isn't used and is automatically set by the mapping layer. The name length restriction is a minimum and could be changed, i.e. it's a placeholder. I'd handle discounts elsewhere, as that is more of a per-application concept. Price should definitely be an integer. Instead of storing 1.00, 100 is stored, so the cent information is maintained. Formatting the price is view-related.
          $endgroup$
          – ChickenFarmer
          Jan 10 '16 at 17:21






          $begingroup$
          Alex, thanks for the response. It has been quite some time since I wrote this code and have improved a number of things since then. The ID is set explicitly on creation just for initialization sake. It isn't used and is automatically set by the mapping layer. The name length restriction is a minimum and could be changed, i.e. it's a placeholder. I'd handle discounts elsewhere, as that is more of a per-application concept. Price should definitely be an integer. Instead of storing 1.00, 100 is stored, so the cent information is maintained. Formatting the price is view-related.
          $endgroup$
          – ChickenFarmer
          Jan 10 '16 at 17:21














          $begingroup$
          Maybe my post was of no use, and if I could, I'd return the rep I received from the bounty (which I'd be fine with, I'd rather have my post be helpful than get the rep.) I can't believe I missed the integer for price thing. That's the way to go and I use that method myself anyways.
          $endgroup$
          – Alex L
          Jan 12 '16 at 4:42




          $begingroup$
          Maybe my post was of no use, and if I could, I'd return the rep I received from the bounty (which I'd be fine with, I'd rather have my post be helpful than get the rep.) I can't believe I missed the integer for price thing. That's the way to go and I use that method myself anyways.
          $endgroup$
          – Alex L
          Jan 12 '16 at 4:42


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f61607%2fmini-ecommerce-application-and-message-warning-error-handling%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          is 'sed' thread safeWhat should someone know about using Python scripts in the shell?Nexenta bash script uses...

          How do i solve the “ No module named 'mlxtend' ” issue on Jupyter?

          Pilgersdorf Inhaltsverzeichnis Geografie | Geschichte | Bevölkerungsentwicklung | Politik | Kultur...