SlideShare ist ein Scribd-Unternehmen logo
1 von 26
Downloaden Sie, um offline zu lesen
A Cute app deserves a
Clean architecture
Marco Piccolino
developer, consultant & trainer
http://marcopiccolino.eu
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 1
Bricks, doors and windows
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 2
Bricks, doors and windows
• Qt provides bricks, doors, windows etc.
• Equivalent to a DIY megastore
• What you do with those components is your problem
• That's a good thing, as long as you have a blueprint for your
application
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 3
Blueprints
Fair use, https://en.wikipedia.org/w/index.php?curid=29612930
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 4
Blueprints
What are the rooms I need? How do they connect to eachother?
Not much literature on application architecture approaches for Qt.
Interesting examples:
A Multilayered Architecture for Qt Quick - David Johnson
http://bit.ly/QtQuickMultilayeredJohnson
QML Application Architecture Guide with Flux – Ben Lau
http://bit.ly/QmlFluxBenlau
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 5
Clean architecture
Credits: http://timshapkin.livejournal.com/16693.html
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 6
Clean architecture (Robert C. Martin)
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 7
Clean architecture: advantages
• Independent of Frameworks
• Testable
• Independent of UI
• Independent of Database
• Independent of external agencies
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 8
A cute implementation
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 9
Application layers
Use cases
Start from here to model application-specific business logic, i.e.
interactions between entities
Entities
Business objects - for me they arise by reasoning about use cases
Repositories
They abstract & encapsulate data I/O
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 10
Use cases / 1
Can be modeled via Behaviour-Driven Development's features and
scenarios. E.g.:
Feature: Check available groceries
I want to check groceries available in my fridge
to know when to buy them before I run out of them
Scenario: One or more grocery items available
Given the list of available grocery items is empty
And one or more grocery items are available
When I check available groceries
Then I am given the list of available grocery items
And the grocery items are ordered by type, ascending
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 11
Use cases / 2
Preconditions (Given), actions (When) and expected results (Then)
can modeled in QtTest or other testing frameworks.
void Usecases_check_available_groceries::test_one_or_more_grocery_items_available()
{
// Given the list of available grocery items is empty
auto groceryItems = new entities::GroceryItems(this);
QCOMPARE(groceryItems->list().count(), 0);
// And one or more grocery items are available
auto groceryItemsDummy = new repositories::GroceryItemsDummy(groceryItems);
QVERIFY(groceryItemsDummy->count() > 0);
groceryItems->setRepository(groceryItemsDummy);
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 12
// When I check available groceries
auto checkAvailableGroceries =
new usecases::CheckAvailableGroceries(groceryItems, this);
QSignalSpy checkAvailableGroceriesSuccess(
checkAvailableGroceries, SIGNAL(success(QString)));
checkAvailableGroceries->run();
QTRY_COMPARE_WITH_TIMEOUT(checkAvailableGroceriesSuccess.count(), 1, 1000);
// Then I am given the list of available grocery items
QCOMPARE(groceryItems->list().count(), groceryItemsDummy->count());
// And the grocery items are ordered by type, ascending
QVERIFY(groceryItems->isSortedBy("type","ASC"));
}
Among other benefits, writing the tests first helps defining a clean
API for usecases, entities and repositories.
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 13
Use cases / 3
• Entities can be passed to usecases as arguments:
checkAvailableGroceries->run(groceryItems);
• Alternatively, a global entity register can be created, and use
cases get hold of entities from there
• By creating mock repositories instead of real ones, use case tests
run fast:
auto groceryItemsDummy = new
repositories::GroceryItemsDummy(groceryItems);
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 14
Use cases / 4
Running the use case: a sequence of interactions with and between
entities.
void CheckAvailableGroceries::run(entities::GroceryItems *groceryItems)
{
connect(groceryItems, &entities::GroceryItems::allRetrieved,
this, &CheckAvailableGroceries::onGroceryItemsAllRetrieved,
Qt::UniqueConnection);
connect(groceryItems ,&entities::GroceryItems::allNotRetrieved,
this, &CheckAvailableGroceries::onGroceryItemsAllNotRetrieved,
Qt::UniqueConnection);
groceryItems->retrieveAll();
}
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 15
Use cases / 5
Once all business logic for the use case is done, we emit a signal:
void CheckAvailableGroceries::onGroceryItemsAllRetrieved() {
emit success("CHECK_AVAILABLE_GROCERIES__SUCCESS");
}
void CheckAvailableGroceries::onGroceryItemsAllNotRetrieved() {
emit failure("CHECK_AVAILABLE_GROCERIES__FAILURE");
}
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 16
Entities / 1
• The API of entities arises from use cases or is already given:
groceryItems->retrieveAll();
• Entities only implement business rules, and leave storage and data
retrieval concerns to repositories:
void GroceryItems::retrieveAll()
{
if (m_repository) {
m_repository->retrieveAllRecords();
}
}
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 17
Entities / 2
Once the data is retrieved, an entity performs its business logic:
void GroceryItems::onAllRecordsRetrieved(QVariantList records)
{
// sort by type ascending
...
// add records to list
m_list->clear();
m_list->append(records);
emit allRetrieved("ENTITIES_GROCERY_ITEMS_ALL_RETRIEVED");
}
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 18
Repositories / 1
Repositories encapsulate data storage, input and output.
Thanks to this, dummy repositories (test doubles) can be employed
while testing use cases and entities.
Benefits:
* Avoids coupling a specific technology (e.g. SQL, REST) to
application logic
* Much faster execution of use cases and entities test suites
* Can switch backends as needed
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 19
Repositories / 2
class GroceryItemsDummy : public GroceryItemsRepo
{
Q_OBJECT
public:
explicit GroceryItemsDummy(QObject *parent = nullptr)
:GroceryItemsRepo(parent){}
int count() const { return 3; }
void retrieveAllRecords() {
QVariantList recordsArray;
recordsArray.push_back(QVariantMap{{"type", "bananas"}});
recordsArray.push_back(QVariantMap{{"type", "apples"}});
recordsArray.push_back(QVariantMap{{"type", "cheese"}});
emit allRecordsRetrieved(recordsArray);
}
};
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 20
Adding a CLI
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 21
Adding a CLI / 1
Once the use case is complete, we can add all kinds of user
interfaces on top of it:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
auto groceryItems = new entities::GroceryItems(&a);
auto groceryItemsDummy = new repositories::GroceryItemsDummy(&a);
auto checkAvailableGroceries = new usecases::CheckAvailableGroceries(&a);
groceryItems->setRepository(groceryItemsDummy);
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 22
Adding a CLI / 2
Text output:
QTextStream cout(stdout);
QObject::connect(checkAvailableGroceries,
&usecases::CheckAvailableGroceries::success,
[&cout, groceryItems](QString message) {
cout << message << endl;
auto list = groceryItems->list();
QVariantList::const_iterator i;
for (i = list.constBegin(); i != list.constEnd(); ++i)
cout << i->toMap().value("type").toString() << endl;
});
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 23
Adding a CLI / 3
Text input:
(cout << "Enter action: ").flush();
QTextStream cin(stdin);
QString action(cin.readLine());
if (action == "check available groceries") {
checkAvailableGroceries->run(groceryItems);
a.exit(0);
} else {
cout << "Action not supported" << endl;
a.exit(1);
}
}
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 24
Considerations
• For thin clients, one or more layers could be written in QML/JS
• This process seems tedious at first, but gains come later on
(debugging, refactoring, new features, new user interfaces)
• You might want to add more layers (e.g. Presenters).
• I went through a few iterations and am still refining the reasoning.
Would love feedback!
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 25
Thank you!
The Clean Architecture (blog post)
http://bit.ly/CleanArchBlog
Clean Architecture (book)
http://bit.ly/CleanArchBook
Code examples
https://github.com/marco-piccolino
Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 26

Weitere ähnliche Inhalte

Ähnlich wie A Cute app deserves a Clean architecture

Diving into VS 2015 Day3
Diving into VS 2015 Day3Diving into VS 2015 Day3
Diving into VS 2015 Day3
Akhil Mittal
 
Requirement:HW6 Problem 2 Design a mobile robot capa.docx
Requirement:HW6 Problem 2 Design a mobile robot capa.docxRequirement:HW6 Problem 2 Design a mobile robot capa.docx
Requirement:HW6 Problem 2 Design a mobile robot capa.docx
audeleypearl
 
Interview questions in qtp
Interview questions in qtpInterview questions in qtp
Interview questions in qtp
Ramu Palanki
 
0205 Product Design - Part II.docxProduct Design – Part II.docx
0205 Product Design - Part II.docxProduct Design – Part II.docx0205 Product Design - Part II.docxProduct Design – Part II.docx
0205 Product Design - Part II.docxProduct Design – Part II.docx
mercysuttle
 
What are the features in qtp
What are the features in qtpWhat are the features in qtp
What are the features in qtp
Ramu Palanki
 
Qtp material for beginners
Qtp material for beginnersQtp material for beginners
Qtp material for beginners
Ramu Palanki
 

Ähnlich wie A Cute app deserves a Clean architecture (20)

ARCHIMATE Physical layer "My Little PanCake Factory"
ARCHIMATE Physical layer "My Little PanCake Factory"ARCHIMATE Physical layer "My Little PanCake Factory"
ARCHIMATE Physical layer "My Little PanCake Factory"
 
Getting Started With Developing For Apple Watch
Getting Started With Developing For Apple WatchGetting Started With Developing For Apple Watch
Getting Started With Developing For Apple Watch
 
TNA taxonomies 20160525
TNA taxonomies 20160525TNA taxonomies 20160525
TNA taxonomies 20160525
 
VENDCOMPOST(Internet of things based composting machine).pptx
VENDCOMPOST(Internet of things based composting machine).pptxVENDCOMPOST(Internet of things based composting machine).pptx
VENDCOMPOST(Internet of things based composting machine).pptx
 
Connected Products Studio Report
Connected Products Studio ReportConnected Products Studio Report
Connected Products Studio Report
 
Machine Vision On Embedded Platform -Report
Machine Vision On Embedded Platform -ReportMachine Vision On Embedded Platform -Report
Machine Vision On Embedded Platform -Report
 
Sda 8
Sda   8Sda   8
Sda 8
 
Diving into VS 2015 Day3
Diving into VS 2015 Day3Diving into VS 2015 Day3
Diving into VS 2015 Day3
 
Getting Started with Developing for the Apple Watch
Getting Started with Developing for the Apple WatchGetting Started with Developing for the Apple Watch
Getting Started with Developing for the Apple Watch
 
Requirement:HW6 Problem 2 Design a mobile robot capa.docx
Requirement:HW6 Problem 2 Design a mobile robot capa.docxRequirement:HW6 Problem 2 Design a mobile robot capa.docx
Requirement:HW6 Problem 2 Design a mobile robot capa.docx
 
The Economics of Microservices (2017 CraftConf)
The Economics of Microservices  (2017 CraftConf)The Economics of Microservices  (2017 CraftConf)
The Economics of Microservices (2017 CraftConf)
 
Interview questions in qtp
Interview questions in qtpInterview questions in qtp
Interview questions in qtp
 
Architecting Single Activity Applications (With or Without Fragments)
Architecting Single Activity Applications (With or Without Fragments)Architecting Single Activity Applications (With or Without Fragments)
Architecting Single Activity Applications (With or Without Fragments)
 
Inversion of control using dependency injection in Web APIs using Unity Conta...
Inversion of control using dependency injection in Web APIs using Unity Conta...Inversion of control using dependency injection in Web APIs using Unity Conta...
Inversion of control using dependency injection in Web APIs using Unity Conta...
 
Continuous delivery for native apps
Continuous delivery for native appsContinuous delivery for native apps
Continuous delivery for native apps
 
SF webinar - The truth curve applied
SF webinar - The truth curve appliedSF webinar - The truth curve applied
SF webinar - The truth curve applied
 
0205 Product Design - Part II.docxProduct Design – Part II.docx
0205 Product Design - Part II.docxProduct Design – Part II.docx0205 Product Design - Part II.docxProduct Design – Part II.docx
0205 Product Design - Part II.docxProduct Design – Part II.docx
 
What are the features in qtp
What are the features in qtpWhat are the features in qtp
What are the features in qtp
 
Qtp material for beginners
Qtp material for beginnersQtp material for beginners
Qtp material for beginners
 
Qtp faqs
Qtp faqsQtp faqs
Qtp faqs
 

Kürzlich hochgeladen

%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
masabamasaba
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
VictoriaMetrics
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
chiefasafspells
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
masabamasaba
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 

Kürzlich hochgeladen (20)

%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 

A Cute app deserves a Clean architecture

  • 1. A Cute app deserves a Clean architecture Marco Piccolino developer, consultant & trainer http://marcopiccolino.eu Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 1
  • 2. Bricks, doors and windows Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 2
  • 3. Bricks, doors and windows • Qt provides bricks, doors, windows etc. • Equivalent to a DIY megastore • What you do with those components is your problem • That's a good thing, as long as you have a blueprint for your application Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 3
  • 4. Blueprints Fair use, https://en.wikipedia.org/w/index.php?curid=29612930 Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 4
  • 5. Blueprints What are the rooms I need? How do they connect to eachother? Not much literature on application architecture approaches for Qt. Interesting examples: A Multilayered Architecture for Qt Quick - David Johnson http://bit.ly/QtQuickMultilayeredJohnson QML Application Architecture Guide with Flux – Ben Lau http://bit.ly/QmlFluxBenlau Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 5
  • 6. Clean architecture Credits: http://timshapkin.livejournal.com/16693.html Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 6
  • 7. Clean architecture (Robert C. Martin) Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 7
  • 8. Clean architecture: advantages • Independent of Frameworks • Testable • Independent of UI • Independent of Database • Independent of external agencies Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 8
  • 9. A cute implementation Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 9
  • 10. Application layers Use cases Start from here to model application-specific business logic, i.e. interactions between entities Entities Business objects - for me they arise by reasoning about use cases Repositories They abstract & encapsulate data I/O Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 10
  • 11. Use cases / 1 Can be modeled via Behaviour-Driven Development's features and scenarios. E.g.: Feature: Check available groceries I want to check groceries available in my fridge to know when to buy them before I run out of them Scenario: One or more grocery items available Given the list of available grocery items is empty And one or more grocery items are available When I check available groceries Then I am given the list of available grocery items And the grocery items are ordered by type, ascending Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 11
  • 12. Use cases / 2 Preconditions (Given), actions (When) and expected results (Then) can modeled in QtTest or other testing frameworks. void Usecases_check_available_groceries::test_one_or_more_grocery_items_available() { // Given the list of available grocery items is empty auto groceryItems = new entities::GroceryItems(this); QCOMPARE(groceryItems->list().count(), 0); // And one or more grocery items are available auto groceryItemsDummy = new repositories::GroceryItemsDummy(groceryItems); QVERIFY(groceryItemsDummy->count() > 0); groceryItems->setRepository(groceryItemsDummy); Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 12
  • 13. // When I check available groceries auto checkAvailableGroceries = new usecases::CheckAvailableGroceries(groceryItems, this); QSignalSpy checkAvailableGroceriesSuccess( checkAvailableGroceries, SIGNAL(success(QString))); checkAvailableGroceries->run(); QTRY_COMPARE_WITH_TIMEOUT(checkAvailableGroceriesSuccess.count(), 1, 1000); // Then I am given the list of available grocery items QCOMPARE(groceryItems->list().count(), groceryItemsDummy->count()); // And the grocery items are ordered by type, ascending QVERIFY(groceryItems->isSortedBy("type","ASC")); } Among other benefits, writing the tests first helps defining a clean API for usecases, entities and repositories. Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 13
  • 14. Use cases / 3 • Entities can be passed to usecases as arguments: checkAvailableGroceries->run(groceryItems); • Alternatively, a global entity register can be created, and use cases get hold of entities from there • By creating mock repositories instead of real ones, use case tests run fast: auto groceryItemsDummy = new repositories::GroceryItemsDummy(groceryItems); Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 14
  • 15. Use cases / 4 Running the use case: a sequence of interactions with and between entities. void CheckAvailableGroceries::run(entities::GroceryItems *groceryItems) { connect(groceryItems, &entities::GroceryItems::allRetrieved, this, &CheckAvailableGroceries::onGroceryItemsAllRetrieved, Qt::UniqueConnection); connect(groceryItems ,&entities::GroceryItems::allNotRetrieved, this, &CheckAvailableGroceries::onGroceryItemsAllNotRetrieved, Qt::UniqueConnection); groceryItems->retrieveAll(); } Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 15
  • 16. Use cases / 5 Once all business logic for the use case is done, we emit a signal: void CheckAvailableGroceries::onGroceryItemsAllRetrieved() { emit success("CHECK_AVAILABLE_GROCERIES__SUCCESS"); } void CheckAvailableGroceries::onGroceryItemsAllNotRetrieved() { emit failure("CHECK_AVAILABLE_GROCERIES__FAILURE"); } Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 16
  • 17. Entities / 1 • The API of entities arises from use cases or is already given: groceryItems->retrieveAll(); • Entities only implement business rules, and leave storage and data retrieval concerns to repositories: void GroceryItems::retrieveAll() { if (m_repository) { m_repository->retrieveAllRecords(); } } Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 17
  • 18. Entities / 2 Once the data is retrieved, an entity performs its business logic: void GroceryItems::onAllRecordsRetrieved(QVariantList records) { // sort by type ascending ... // add records to list m_list->clear(); m_list->append(records); emit allRetrieved("ENTITIES_GROCERY_ITEMS_ALL_RETRIEVED"); } Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 18
  • 19. Repositories / 1 Repositories encapsulate data storage, input and output. Thanks to this, dummy repositories (test doubles) can be employed while testing use cases and entities. Benefits: * Avoids coupling a specific technology (e.g. SQL, REST) to application logic * Much faster execution of use cases and entities test suites * Can switch backends as needed Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 19
  • 20. Repositories / 2 class GroceryItemsDummy : public GroceryItemsRepo { Q_OBJECT public: explicit GroceryItemsDummy(QObject *parent = nullptr) :GroceryItemsRepo(parent){} int count() const { return 3; } void retrieveAllRecords() { QVariantList recordsArray; recordsArray.push_back(QVariantMap{{"type", "bananas"}}); recordsArray.push_back(QVariantMap{{"type", "apples"}}); recordsArray.push_back(QVariantMap{{"type", "cheese"}}); emit allRecordsRetrieved(recordsArray); } }; Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 20
  • 21. Adding a CLI Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 21
  • 22. Adding a CLI / 1 Once the use case is complete, we can add all kinds of user interfaces on top of it: int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); auto groceryItems = new entities::GroceryItems(&a); auto groceryItemsDummy = new repositories::GroceryItemsDummy(&a); auto checkAvailableGroceries = new usecases::CheckAvailableGroceries(&a); groceryItems->setRepository(groceryItemsDummy); Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 22
  • 23. Adding a CLI / 2 Text output: QTextStream cout(stdout); QObject::connect(checkAvailableGroceries, &usecases::CheckAvailableGroceries::success, [&cout, groceryItems](QString message) { cout << message << endl; auto list = groceryItems->list(); QVariantList::const_iterator i; for (i = list.constBegin(); i != list.constEnd(); ++i) cout << i->toMap().value("type").toString() << endl; }); Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 23
  • 24. Adding a CLI / 3 Text input: (cout << "Enter action: ").flush(); QTextStream cin(stdin); QString action(cin.readLine()); if (action == "check available groceries") { checkAvailableGroceries->run(groceryItems); a.exit(0); } else { cout << "Action not supported" << endl; a.exit(1); } } Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 24
  • 25. Considerations • For thin clients, one or more layers could be written in QML/JS • This process seems tedious at first, but gains come later on (debugging, refactoring, new features, new user interfaces) • You might want to add more layers (e.g. Presenters). • I went through a few iterations and am still refining the reasoning. Would love feedback! Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 25
  • 26. Thank you! The Clean Architecture (blog post) http://bit.ly/CleanArchBlog Clean Architecture (book) http://bit.ly/CleanArchBook Code examples https://github.com/marco-piccolino Marco Piccolino - A Cute app deserves a Clean architecture - http://marcopiccolino.eu - hello@marcopiccolino.eu 26