Clean architecture and c++

986 views
Skip to first unread message

Bennie Copeland

unread,
Aug 16, 2013, 9:20:39 AM8/16/13
to clean-code...@googlegroups.com
Hello all. I'm just getting started with clean architecture on a personal project and have some questions. From what I have read and the videos I've seen, the interactor creates the entity objects and gives them to the persistence layer to store, but who "owns" the objects? In a managed language like c# (where most of my experience lies), I don't have to worry about the memory management usually. With me writing this in c++, I have to take care to not introduce memory leaks at the same time as trying to learn c++ and clean architecture. I'm getting really confused on the best way to pass these objects/classes around and how to manage their lifetimes. This is what I have, but I don't know if it is correct.

int main()
{
   
Persistence persistence;

   
AddOrder addOrder("My order", persistence);
    addOrder
.Execute();

   
const unique_ptr<Order> &order = persistence.GetOrder("My order");

   
AddLineItem lineItem("My Lineitem", order, persistence);
    lineItem
.Execute();

   
return 0;
}

class Persistence
{
public:
    unique_ptr
<Order> const& GetOrder(std::string OrderName)
   
{
       
return orders[OrderName];
   
};

   
void AddOrder(unique_ptr<Order> order)
   
{
        orders
[order->Name] = move(order);
   
};
private:
    std
::Map<std::string, unique_ptr<Order> > orders;
}

class AddOrder
{
public:
   
AddOrder(string OrderName, const Persistence &persistence)
   
{
       
this->OrderName = OrderName;
       
this->persistence = persistence;
   
};

   
void Execute()
   
{
        std
::unique_ptr<Order> order = std::unique_ptr<Order>(new Order(OrderName));

        persistence
.AddOrder(move(order));
   
};
private:
    std
::string OrderName;
   
Persistence persistence;
}

class AddLineItem
{
public:
   
AddLineItem(const std::string &LineItemName, const unique_ptr<Order> &order, const Persistence &persistence)
   
{
       
// store parameters
   
}

   
void Execute()
   
{
        unique_ptr
<LineItem> lineItem = unique_ptr<LineItem>(new LineItem(lineItemName));

        order
.AddLineItem(move(lineItem));
   
}
}

class Order
{
public:
   
Order(const std::string &OrderName) { /*store name*/ };

   
void AddLineItem(unique_ptr<LineItem> lineItem)
   
{
        lineItems
[lineItem->GetName] = lineItem;
   
}

   
string GetName() { return Name; };
private:
    map
<string, unique_ptr<LineItem> > lineItems;
}

class LineItem
{
public:
   
LineItem(const std::string &ItemName) { /*store name*/ };
   
string GetName() { return name; };
}

  1. Is my assumption correct that it is the persistence layer that "owns" the root level object of Order, but Order should "own" its LineItems, and LineItems should own their children, etc.
  2. Am I doing it right by creating Order and LineItem as a unique_ptr and using std::move to transfer ownership to the persistence layer?
  3. What should I be returning from Persistence::GetOrder() and Persistence::GetLineItem()? A const reference to the stored unique_ptr Order/LineItem, a struct with just the values?
  4. If I am returning the Order object from Persistence::GetOrder(), what prevents the delivery layer from just going directly to the object in the persistence class and adding a new lineitem and bypassing the AddLineItem interactor alltogether?
  5. The app layer is only supposed to return DTOs to the delivery layer (as I understand it), how would you pass a complex model up? Say a model of managers and employees where an employee could also be a manager, so you have a tree structure.
In c#, alot of this lifetime management is handled for me and I am having a hard time figuring this out in c++.

Uncle Bob

unread,
Aug 21, 2013, 9:50:52 AM8/21/13
to clean-code...@googlegroups.com
In general, it is the interactors that deal with the complexities of transaction management.  They know when entitles are needed, and when they aren't needed anymore.  Memory management is closely related to transaction management, but is at a lower level.  So I'd put the memory management code down below the gateways.  Let the interactors tell the gateways about the transactions, and let the gateways use the transactions as a way to manage memory.  That way the gateways can do caching if necessary, without any of the interactors or entities knowing about it.
Reply all
Reply to author
Forward
0 new messages