-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathpreface.html
More file actions
638 lines (622 loc) · 28.7 KB
/
preface.html
File metadata and controls
638 lines (622 loc) · 28.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.10">
<title>Preface</title>
<style>
/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
@import url("//fonts.googleapis.com/css?family=Noto+Sans:300,600italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700");
@import url(//asciidoctor.org/stylesheets/asciidoctor.css); /* Default asciidoc style framework - important */
/* customisations by harry */
/* hide inline ditaa/plantuml source listings for images */
.image-source {
display: none
}
/* make formal codeblocks a bit nicer */
.exampleblock > .content {
padding: 2px;
background-color: white;
border: 0;
margin-bottom: 2em;
}
.exampleblock .title {
text-align: right;
}
/* end customisations by harry */
/* CUSTOMISATIONS */
/* Change the values in root for quick customisation. If you want even more fine grain... venture further. */
:root{
--maincolor:#FFFFFF;
--primarycolor:#2c3e50;
--secondarycolor:#ba3925;
--tertiarycolor: #186d7a;
--sidebarbackground:#CCC;
--linkcolor:#b71c1c;
--linkcoloralternate:#f44336;
--white:#FFFFFF;
--black:#000000;
}
/* Text styles */
h1{color:var(--primarycolor) !important;}
h2,h3,h4,h5,h6{color:var(--secondarycolor) !important;}
.title{color:var(--tertiarycolor) !important; font-family:"Noto Sans",sans-serif !important;font-style: normal !important; font-weight: normal !important;}
p{font-family: "Noto Sans",sans-serif !important}
/* Table styles */
th{font-family: "Noto Sans",sans-serif !important}
/* Responsiveness fixes */
video {
max-width: 100%;
}
@media all and (max-width: 600px) {
table {
width: 55vw!important;
font-size: 3vw;
}
</style>
</head>
<body class="article toc2 toc-left">
<div id="buy_the_book" style="position: absolute; top: 0; right: 0; z-index:100">
<a href="/#buy_the_book">
<img src="/images/buy_the_book.svg" alt="buy the book ribbon">
</a>
</div>
<div id="header">
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="/book/preface.html">Preface</a></li>
<li><a href="/book/introduction.html">Introduction</a></li>
<li><a href="/book/part1.html">Building an Architecture to Support Domain Modeling</a></li>
<li><a href="/book/chapter_01_domain_model.html">1. Domain Modeling</a></li>
<li><a href="/book/chapter_02_repository.html">2. Repository Pattern</a></li>
<li><a href="/book/chapter_03_abstractions.html">3. A Brief Interlude: On Coupling <span class="keep-together">and Abstractions</span></a></li>
<li><a href="/book/chapter_04_service_layer.html">4. Our First Use Case: <span class="keep-together">Flask API and Service Layer</span></a></li>
<li><a href="/book/chapter_05_high_gear_low_gear.html">5. TDD in High Gear and Low Gear</a></li>
<li><a href="/book/chapter_06_uow.html">6. Unit of Work Pattern</a></li>
<li><a href="/book/chapter_07_aggregate.html">7. Aggregates and Consistency Boundaries</a></li>
<li><a href="/book/part2.html">Event-Driven Architecture</a></li>
<li><a href="/book/chapter_08_events_and_message_bus.html">8. Events and the Message Bus</a></li>
<li><a href="/book/chapter_09_all_messagebus.html">9. Going to Town on the Message Bus</a></li>
<li><a href="/book/chapter_10_commands.html">10. Commands and Command Handler</a></li>
<li><a href="/book/chapter_11_external_events.html">11. Event-Driven Architecture: Using Events to Integrate Microservices</a></li>
<li><a href="/book/chapter_12_cqrs.html">12. Command-Query Responsibility Segregation (CQRS)</a></li>
<li><a href="/book/chapter_13_dependency_injection.html">13. Dependency Injection (and Bootstrapping)</a></li>
<li><a href="/book/epilogue_1_how_to_get_there_from_here.html">Appendix A: Epilogue</a></li>
<li><a href="/book/appendix_ds1_table.html">Appendix B: Summary Diagram and Table</a></li>
<li><a href="/book/appendix_project_structure.html">Appendix C: A Template Project Structure</a></li>
<li><a href="/book/appendix_csvs.html">Appendix D: Swapping Out the Infrastructure: <span class="keep-together">Do Everything with CSVs</span></a></li>
<li><a href="/book/appendix_django.html">Appendix E: Repository and Unit of Work <span class="keep-together">Patterns with Django</span></a></li>
<li><a href="/book/appendix_validation.html">Appendix F: Validation</a></li>
</ul>
</div>
</div>
<div id="content">
<div class="sect1">
<h2 id="preface">Preface</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You may be wondering who we are and why we wrote this book.</p>
</div>
<div class="paragraph">
<p>At the end of Harry’s last book,
<a href="http://obeythetestinggoat.com"><em>Test-Driven Development with Python</em></a> (O’Reilly),
he found himself asking a bunch of questions about architecture, such as,
What’s the best way of structuring your application so that it’s easy to test?
More specifically, so that your core business logic is covered by unit tests,
and so that you minimize the number of integration and end-to-end tests you need?
He made vague references to "Hexagonal Architecture" and "Ports and Adapters"
and "Functional Core, Imperative Shell," but if he was honest, he’d have to
admit that these weren’t things he really understood or had done in practice.</p>
</div>
<div class="paragraph">
<p>And then he was lucky enough to run into Bob, who has the answers to all these
questions.</p>
</div>
<div class="paragraph">
<p>Bob ended up a software architect because nobody else on his team was
doing it. He turned out to be pretty bad at it, but <em>he</em> was lucky enough to run
into Ian Cooper, who taught him new ways of writing and thinking about code.</p>
</div>
<div class="sect2">
<h3 id="_managing_complexity_solving_business_problems">Managing Complexity, Solving Business Problems</h3>
<div class="paragraph">
<p>We both work for MADE.com, a European ecommerce company that sells furniture
online; there, we apply the techniques in this book to build distributed systems
that model real-world business problems. Our example domain is the first system
Bob built for MADE, and this book is an attempt to write down all the <em>stuff</em> we
have to teach new programmers when they join one of our teams.</p>
</div>
<div class="paragraph">
<p>MADE.com operates a global supply chain of freight partners and manufacturers.
To keep costs low, we try to optimize the delivery of stock to our
warehouses so that we don’t have unsold goods lying around the place.</p>
</div>
<div class="paragraph">
<p>Ideally, the sofa that you want to buy will arrive in port on the very day
that you decide to buy it, and we’ll ship it straight to your house without
ever storing it. <span class="keep-together">Getting</span> the timing right is a tricky balancing act when goods take
three months to arrive by container ship. Along the way, things get broken or water
damaged, storms cause unexpected delays, logistics partners mishandle goods,
paperwork goes missing, customers change their minds and amend their orders,
and so on.</p>
</div>
<div class="paragraph">
<p>We solve those problems by building intelligent software representing the
kinds of operations taking place in the real world so that we can automate as
much of the business as possible.</p>
</div>
</div>
<div class="sect2">
<h3 id="_why_python">Why Python?</h3>
<div class="paragraph">
<p>If you’re reading this book, we probably don’t need to convince you that Python
is great, so the real question is "Why does the <em>Python</em> community need a book
like this?" The answer is about Python’s popularity and maturity: although Python is
probably the world’s fastest-growing programming language and is nearing the top
of the absolute popularity tables, it’s only just starting to take on the kinds
of problems that the C# and Java world has been working on for years.
Startups become real businesses; web apps and scripted automations are becoming
(whisper it) <em>enterprise</em> <span class="keep-together"><em>software</em></span>.</p>
</div>
<div class="paragraph">
<p>In the Python world, we often quote the Zen of Python:
"There should be one—​and preferably only one—​obvious way to do it."<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>
Unfortunately, as project size grows, the most obvious way of doing things
isn’t always the way that helps you manage complexity and evolving
requirements.</p>
</div>
<div class="paragraph">
<p>None of the techniques and patterns we discuss in this book are
new, but they are mostly new to the Python world. And this book isn’t
a replacement for the classics in the field such as Eric Evans’s
<em>Domain-Driven Design</em>
or Martin Fowler’s <em>Patterns of
Enterprise Application Architecture</em> (both published by Addison-Wesley <span class="keep-together">Professional</span>)—which we often refer to and
encourage you to go and read.</p>
</div>
<div class="paragraph">
<p>But all the classic code examples in the literature do tend to be written in
Java or <span class="keep-together">C++/#</span>, and if you’re a Python person and haven’t used either of
those languages in a long time (or indeed ever), those code listings can be
quite…​trying. There’s a reason the latest edition of that other classic text, Fowler’s
<em>Refactoring</em> (Addison-Wesley Professional), is in JavaScript.</p>
</div>
</div>
<div class="sect2 pagebreak-before less_space">
<h3 id="_tdd_ddd_and_event_driven_architecture">TDD, DDD, and Event-Driven Architecture</h3>
<div class="paragraph">
<p>In order of notoriety, we know of three tools for managing complexity:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><em>Test-driven development</em> (TDD) helps us to build code that is correct
and enables us to refactor or add new features, without fear of regression.
But it can be hard to get the best out of our tests: How do we make sure
that they run as fast as possible? That we get as much coverage and feedback
from fast, dependency-free unit tests and have the minimum number of slower,
flaky end-to-end tests?</p>
</li>
<li>
<p><em>Domain-driven design</em> (DDD) asks us to focus our efforts on building a good
model of the business domain, but how do we make sure that our models aren’t
encumbered with infrastructure concerns and don’t become hard to change?</p>
</li>
<li>
<p>Loosely coupled (micro)services integrated via messages (sometimes called
<em>reactive microservices</em>) are a well-established answer to managing complexity
across multiple applications or business domains. But it’s not always
obvious how to make them fit with the established tools of
the Python world—​Flask, Django, Celery, and so on.</p>
</li>
</ol>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
Don’t be put off if you’re not working with (or interested in) microservices. The vast majority of the patterns we discuss, including much of the event-driven architecture material, is absolutely applicable in a monolithic architecture.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Our aim with this book is to introduce several classic architectural patterns
and show how they support TDD, DDD, and event-driven services. We hope
it will serve as a reference for implementing them in a Pythonic way, and that
people can use it as a first step toward further research in this field.</p>
</div>
</div>
<div class="sect2">
<h3 id="_who_should_read_this_book">Who Should Read This Book</h3>
<div class="paragraph">
<p>Here are a few things we assume about you, dear reader:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>You’ve been close to some reasonably complex Python applications.</p>
</li>
<li>
<p>You’ve seen some of the pain that comes with trying to manage
that complexity.</p>
</li>
<li>
<p>You don’t necessarily know anything about DDD or any of the
classic application architecture patterns.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>We structure our explorations of architectural patterns around an example app,
building it up chapter by chapter. We use TDD at
work, so we tend to show listings of tests first, followed by implementation.
If you’re not used to working test-first, it may feel a little strange at
the beginning, but we hope you’ll soon get used to seeing code "being used"
(i.e., from the outside) before you see how it’s built on the inside.</p>
</div>
<div class="paragraph">
<p>We use some specific Python frameworks and technologies, including Flask,
SQLAlchemy, and pytest, as well as Docker and Redis. If you’re already
familiar with them, that won’t hurt, but we don’t think it’s required. One of
our main aims with this book is to build an architecture for which specific
technology choices become minor implementation details.</p>
</div>
</div>
<div class="sect2">
<h3 id="_a_brief_overview_of_what_youll_learn">A Brief Overview of What You’ll Learn</h3>
<div class="paragraph">
<p>The book is divided into two parts; here’s a look at the topics we’ll cover
and the chapters they live in.</p>
</div>
<div class="sect3">
<h4 id="_part1"><a data-type="xref" data-xrefstyle="chap-num-title" href="/book/part1.html">#part1</a></h4>
<div class="dlist">
<dl>
<dt class="hdlist1">Domain modeling and DDD (Chapters <a data-type="xref" data-xrefstyle="select:labelnumber" href="/book/chapter_01_domain_model.html">#chapter_01_domain_model</a> and <a data-type="xref" data-xrefstyle="select:labelnumber" href="/book/chapter_07_aggregate.html">#chapter_07_aggregate</a>)</dt>
<dd>
<p>At some level, everyone has learned the lesson that complex business
problems need to be reflected in code, in the form of a model of the domain.
But why does it always seem to be so hard to do without getting tangled
up with infrastructure concerns, our web frameworks, or whatever else?
In the first chapter we give a broad overview of <em>domain modeling</em> and DDD, and we
show how to get started with a model that has no external dependencies, and
fast unit tests. Later we return to DDD patterns to discuss how to choose
the right aggregate, and how this choice relates to questions of data
integrity.</p>
</dd>
<dt class="hdlist1">Repository, Service Layer, and Unit of Work patterns (Chapters <a data-type="xref" data-xrefstyle="select:labelnumber" href="/book/chapter_02_repository.html">#chapter_02_repository</a>, <a data-type="xref" data-xrefstyle="select:labelnumber" href="/book/chapter_04_service_layer.html">#chapter_04_service_layer</a>, and <a data-type="xref" data-xrefstyle="select:labelnumber" href="/book/chapter_05_high_gear_low_gear.html">#chapter_05_high_gear_low_gear</a>)</dt>
<dd>
<p>In these three chapters we present three closely related and
mutually reinforcing patterns that support our ambition to keep
the model free of extraneous dependencies. We build a layer of
abstraction around persistent storage, and we build a service
layer to define the entrypoints to our system and capture the
primary use cases. We show how this layer makes it easy to build
thin entrypoints to our system, whether it’s a Flask API or a CLI.</p>
</dd>
</dl>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">Some thoughts on testing and abstractions (Chapters <a data-type="xref" data-xrefstyle="select:labelnumber" href="/book/chapter_03_abstractions.html">#chapter_03_abstractions</a> and <a data-type="xref" data-xrefstyle="select:labelnumber" href="/book/chapter_06_uow.html">#chapter_06_uow</a>)</dt>
<dd>
<p>After presenting the first abstraction (the Repository pattern), we take the
opportunity for a general discussion of how to choose abstractions, and
what their role is in choosing how our software is coupled together. After
we introduce the Service Layer pattern, we talk a bit about achieving a <em>test pyramid</em>
and writing unit tests at the highest possible level of abstraction.</p>
</dd>
</dl>
</div>
</div>
<div class="sect3">
<h4 id="_part2"><a data-type="xref" data-xrefstyle="chap-num-title" href="/book/part2.html">#part2</a></h4>
<div class="dlist">
<dl>
<dt class="hdlist1">Event-driven architecture (Chapters <a data-type="xref" data-xrefstyle="select:labelnumber" href="/book/chapter_08_events_and_message_bus.html">#chapter_08_events_and_message_bus</a>–<a data-type="xref" data-xrefstyle="select:labelnumber" href="/book/chapter_11_external_events.html">#chapter_11_external_events</a>)</dt>
<dd>
<p>We introduce three more mutually reinforcing patterns: the Domain Events, Message Bus, and Handler patterns. <em>Domain events</em> are a vehicle for capturing the idea that some
interactions with a system are triggers for others. We use a <em>message
bus</em> to allow actions to trigger events and call appropriate <em>handlers</em>.
We move on to discuss how events can be used as a pattern for integration
between services in a microservices architecture. Finally, we distinguish between <em>commands</em> and <em>events</em>. Our application is now
fundamentally a message-processing system.</p>
</dd>
<dt class="hdlist1">Command-query responsibility segregation (<a href="/book/chapter_12_cqrs.html">[chapter_12_cqrs]</a>)</dt>
<dd>
<p>We present an example of <em>command-query responsibility segregation</em>, with and without
events.</p>
</dd>
<dt class="hdlist1">Dependency injection (<a href="/book/chapter_13_dependency_injection.html">[chapter_13_dependency_injection]</a>)</dt>
<dd>
<p>We tidy up our explicit and implicit dependencies and implement a
simple dependency injection framework.</p>
</dd>
</dl>
</div>
</div>
<div class="sect3">
<h4 id="_addtional_content">Addtional Content</h4>
<div class="dlist">
<dl>
<dt class="hdlist1">How do I get there from here? (<a href="/book/epilogue_1_how_to_get_there_from_here.html">[epilogue_1_how_to_get_there_from_here]</a>)</dt>
<dd>
<p>Implementing architectural patterns always looks easy when you show a simple
example, starting from scratch, but many of you will probably be wondering how
to apply these principles to existing software. We’ll provide a
few pointers in the epilogue and some links to further reading.</p>
</dd>
</dl>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_example_code_and_coding_along">Example Code and Coding Along</h3>
<div class="paragraph">
<p>You’re reading a book, but you’ll probably agree with us when we say that
the best way to learn about code is to code. We learned most of what we know
from pairing with people, writing code with them, and learning by doing, and
we’d like to re-create that experience as much as possible for you in this book.</p>
</div>
<div class="paragraph">
<p>As a result, we’ve structured the book around a single example project
(although we do sometimes throw in other examples). We’ll build up this project as the chapters progress, as if you’ve paired with us and
we’re explaining what we’re doing and why at each step.</p>
</div>
<div class="paragraph">
<p>But to really get to grips with these patterns, you need to mess about with the
code and get a feel for how it works. You’ll find all the code on
GitHub; each chapter has its own branch. You can find <a href="https://github.com/cosmicpython/code/branches/all">a list</a> of the branches on GitHub as well.</p>
</div>
<div class="paragraph pagebreak-before">
<p>Here are three ways you might code along with the book:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Start your own repo and try to build up the app as we do, following the
examples from listings in the book, and occasionally looking to our repo
for hints. A word of warning, however: if you’ve read Harry’s previous book
and coded along with that, you’ll find that this book requires you to figure out more on
your own; you may need to lean pretty heavily on the working versions on GitHub.</p>
</li>
<li>
<p>Try to apply each pattern, chapter by chapter, to your own (preferably
small/toy) project, and see if you can make it work for your use case. This
is high risk/high reward (and high effort besides!). It may take quite some
work to get things working for the specifics of your project, but on the other
hand, you’re likely to learn the most.</p>
</li>
<li>
<p>For less effort, in each chapter we outline an "Exercise for the Reader,"
and point you to a GitHub location where you can download some partially finished
code for the chapter with a few missing parts to write yourself.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Particularly if you’re intending to apply some of these patterns in your own
projects, working through a simple example is a great way to
safely practice.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<div class="title">Tip</div>
</td>
<td class="content">
At the very least, do a <code>git checkout</code> of the code from our repo as you
read each chapter. Being able to jump in and see the code in the context of
an actual working app will help answer a lot of questions as you go, and
makes everything more real. You’ll find instructions for how to do that
at the beginning of each chapter.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_license">License</h3>
<div class="paragraph">
<p>The code (and the online version of the book) is licensed under a Creative
Commons CC BY-NC-ND license, which means you are free to copy and share it with
anyone you like, for non-commercial purposes, as long as you give attribution.
If you want to re-use any of the content from this book and you have any
worries about the license, contact O’Reilly at <a class="email" href="mailto:permissions@oreilly.com"><em>permissions@oreilly.com</em></a>.</p>
</div>
<div class="paragraph">
<p>The print edition is licensed differently; please see the copyright page.</p>
</div>
</div>
<div class="sect2">
<h3 id="_conventions_used_in_this_book">Conventions Used in This Book</h3>
<div class="paragraph">
<p>The following typographical conventions are used in this book:</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1"><em>Italic</em></dt>
<dd>
<p>Indicates new terms, URLs, email addresses, filenames, and file extensions.</p>
</dd>
<dt class="hdlist1">Constant width</dt>
<dd>
<p>Used for program listings, as well as within paragraphs to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords.</p>
</dd>
<dt class="hdlist1"><strong><code>Constant width bold</code></strong></dt>
<dd>
<p>Shows commands or other text that should be typed literally by the user.</p>
</dd>
<dt class="hdlist1"><em>Constant width italic</em></dt>
<dd>
<p>Shows text that should be replaced with user-supplied values or by values determined by context.</p>
</dd>
</dl>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<div class="title">Tip</div>
</td>
<td class="content">
<div class="paragraph">
<p>This element signifies a tip or suggestion.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
<div class="paragraph">
<p>This element signifies a general note.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<div class="title">Warning</div>
</td>
<td class="content">
<div class="paragraph">
<p>This element indicates a warning or caution.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_oreilly_online_learning">O’Reilly Online Learning</h3>
<div class="admonitionblock note ormenabled">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
<div class="paragraph">
<p>For more than 40 years, <a href="http://oreilly.com" class="orm:hideurl"><em class="hyperlink">O’Reilly Media</em></a> has provided technology and business training, knowledge, and insight to help companies succeed.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Our unique network of experts and innovators share their knowledge and expertise through books, articles, conferences, and our online learning platform. O’Reilly’s online learning platform gives you on-demand access to live training courses, in-depth learning paths, interactive coding environments, and a vast collection of text and video from O’Reilly and 200+ other publishers. For more information, please visit <a href="http://oreilly.com" class="orm:hideurl"><em>http://oreilly.com</em></a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_how_to_contact_oreilly">How to Contact O’Reilly</h3>
<div class="paragraph">
<p>Please address comments and questions concerning this book to the publisher:</p>
</div>
<ul class="simplelist">
<li>O’Reilly Media, Inc.</li>
<li>1005 Gravenstein Highway North</li>
<li>Sebastopol, CA 95472</li>
<li>800-998-9938 (in the United States or Canada)</li>
<li>707-829-0515 (international or local)</li>
<li>707-829-0104 (fax)</li>
</ul>
<div class="paragraph">
<p>We have a web page for this book, where we list errata, examples, and any additional information. You can access this page at <a href="https://oreil.ly/architecture-patterns-python" class="bare">https://oreil.ly/architecture-patterns-python</a>.</p>
</div>
<!--Don't forget to update the link above.-->
<div class="paragraph">
<p>Email <a class="email" href="mailto:bookquestions@oreilly.com"><em>bookquestions@oreilly.com</em></a> to comment or ask technical questions about this book.</p>
</div>
<div class="paragraph">
<p>For more information about our books, courses, conferences, and news, see our website at <a href="http://www.oreilly.com" class="bare">http://www.oreilly.com</a>.</p>
</div>
<div class="paragraph">
<p>Find us on Facebook: <a href="http://facebook.com/oreilly" class="bare">http://facebook.com/oreilly</a></p>
</div>
<div class="paragraph">
<p>Follow us on Twitter: <a href="http://twitter.com/oreillymedia" class="bare">http://twitter.com/oreillymedia</a></p>
</div>
<div class="paragraph">
<p>Watch us on YouTube: <a href="http://www.youtube.com/oreillymedia" class="bare">http://www.youtube.com/oreillymedia</a></p>
</div>
</div>
<div class="sect2">
<h3 id="_acknowledgments">Acknowledgments</h3>
<div class="paragraph">
<p>To our tech reviewers, David Seddon, Ed Jung, and Hynek Schlawack: we absolutely
do not deserve you. You are all incredibly dedicated, conscientious, and
rigorous. Each one of you is immensely smart, and your different points of
view were both useful and complementary to each other. Thank you from the
bottom of our hearts.</p>
</div>
<div class="paragraph">
<p>Gigantic thanks also to our Early Release readers for their comments and
suggestions:
Ian Cooper, Abdullah Ariff, Jonathan Meier, Gil Gonçalves, Matthieu Choplin,
Ben Judson, James Gregory, Łukasz Lechowicz, Clinton Roy, Vitorino Araújo,
Susan Goodbody, Josh Harwood, Daniel Butler, Liu Haibin, Jimmy Davies, Ignacio
Vergara Kausel, Gaia Canestrani, Renne Rocha, pedroabi, Ashia Zawaduk, Jostein
Leira, Brandon Rhodes, Jazeps Basko
and many more; our apologies if we missed you on this list.</p>
</div>
<div class="paragraph">
<p>Super-mega-thanks to our editor Corbin Collins for his gentle chivvying, and
for being a tireless advocate of the reader. Similarly-superlative thanks to the production staff, Katherine Tozer, Sharon Wilkey, Ellen Troutman-Zaig, and Rebecca Demarest, for your dedication, professionalism, and attention to detail. This book is immeasurably improved thanks to you.</p>
</div>
<div class="paragraph">
<p>Any errors remaining in the book are our own, naturally.</p>
</div>
</div>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. <code>python -c "import this"</code>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2020-03-10 17:16:05 UTC
</div>
</div>
<div><div id="disqus_thread" style="margin: 10px"></div>
<script>
var disqus_config = function () {
this.page.url = 'https://cosmicpython.com';
this.page.identifier = 'preface';
};
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://cosmic-python.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</div><html><head><script async src="https://www.googletagmanager.com/gtag/js?id=UA-40928035-3"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-40928035-3');
</script>
</head></html></body>
</html>