emacsway-log: Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, XP, Agile, etc. – Telegram
emacsway-log: Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, XP, Agile, etc.
3.48K subscribers
119 photos
15 videos
22 files
1.14K links
Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, Extreme Programming, SDLC, Agile, etc.

Chat: https://news.1rj.ru/str/emacsway_chat

Persistence: https://dckms.github.io/system-architecture/
Download Telegram
📝 "Motivation

Thirty years ago Yourdon and Constantine in Structured Design identified economics as the underlying driver of software design. Software should be designed to reduce its overall cost. The cost of software is divided into the initial cost and the cost of maintenance:

cost total = cost develop + cost maintain

Once the industry gained experience with software development, it was a big surprise to many that the cost of maintenance is much higher than the cost of initial development. (Projects with little or no maintenance should use a very different set of implementation patterns from those presented in the remainder of this book.)

Maintenance is expensive because understanding existing code is timeconsuming and error-prone. Making changes is generally easy when you know what needs changing. Learning what the current code does is the expensive part. Once the changes are made, they need to be tested and deployed.

cost maintain = cost understand + cost change + cost test + cost deploy

One strategy for reducing overall cost is to invest more in initial development in hope of reducing or eliminating the need for maintenance. Such efforts have generally failed to reduce overall costs. When code needs to change in unanticipated ways, no amount of forethought can perfectly prepare the code for change. The premature attempts to make the code general enough to meet future needs often interfere with the unanticipated changes that turn out to be necessary.

Substantially increasing the up-front investment in software runs counter to two important economic principles: the time value of money and the uncertainty of the future. Since a dollar today is worth more than a dollar tomorrow, in principle an implementation strategy should generally encourage deferring costs. Also, because of uncertainty an implementation strategy should generally take immediate benefits in preference over possible long-term benefits. While this may sound like a license to hack without regard for the future, the implementation patterns are focused on ways to gain immediate benefit while setting up clean code for ease of future development.

My strategy for reducing overall costs is to ask all programmers to address the cost of understanding code during the maintenance phase by focusing on communicating, programmer-to-programmer. The immediate benefits of clear code are fewer defects, easier sharing of code, and smoother development. Once a set of implementation patterns has become habitual, I program faster and with fewer distracting thoughts. When I began writing my first set of implementation patterns (The Smalltalk Best Practice Patterns, Prentice Hall 1996) I thought I was a proficient programmer. To encourage myself to focus on patterns, I refused to type a character of code unless I had first written down the pattern I was following. It was frustrating, like I was coding with my fingers glued together. For the first week every minute of coding was preceded by an hour of writing. The second week I found I had most of the basic patterns in place and most of the time I was following existing patterns. By the third week I was coding much faster than I had before, because I had carefully looked at my own style and I wasn’t nagged by doubts.

The implementation patterns that I wrote down were only partly my own invention. Most of my style was copied from earlier generations of programmers. They were the habits that resulted in code that was easier to read, understand, and modify, and by codifying them I was able to code more quickly and fluidly than I had before. I could prepare for the future and get today’s code done faster at the same time."
emacsway-log: Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, XP, Agile, etc.
📝 "Motivation Thirty years ago Yourdon and Constantine in Structured Design identified economics as the underlying driver of software design. Software should be designed to reduce its overall cost. The cost of software is divided into the initial cost and…
For the implementation patterns in this book I looked at existing code as well as my own habits for inspiration. I read and compared code from the JDK, from Eclipse, and from my experience of development. The resulting patterns are intended to be a coherent view of how to write code people can understand. Other points of view or sets of values would result in different patterns. For example, I sketch the implementation patterns for framework development in “Evolving Frameworks”. These are based on a different set of priorities and so vary from my basic implementation style.

The implementation patterns serve human as well as economic needs. Code is by people and for people. Programmers can use the implementation patterns to help satisfy human needs, like the need to take pride in work well done or the need to be a trustworthy member of a community. I discuss the human and economic impact of the patterns as we go in the following chapters."
- "Implementation Patterns" by Kent Beck

#SoftwareDesign
В последнее время наметилась определенная поляризация парадигм программирования в индустрии.

Стремительный рост объема обрабатываемых данных, потребность в масштабировании, распределенном хранении и параллельной обработке данных, пробудили интерес к функциональному программированию.

📝 "Все состояния гонки (race condition), взаимоблокировки (deadlocks) и проблемы параллельного обновления обусловлены изменяемостью переменных. Если в программе нет изменяемых переменных, она никогда не окажется в состоянии гонки и никогда не столкнется с проблемами одновременного изменения. В отсутствие изменяемых блокировок программа не может попасть в состояние взаимоблокировки.

All race conditions, deadlock conditions, and concurrent update problems are due to mutable variables. You cannot have a race condition or a concurrent update problem if no variable is ever updated. You cannot have deadlocks without mutable locks."

- "Clean Architecture: A Craftsman’s Guide to Software Structure and Design" by Robert C. Martin

Однако, индустрия не готова отказаться от императивных подвидов парадигм, таких как OOP.

Можно ли их сочетать, используя достоинства обоих видов парадигм, в зависимости от контекста использования? Как эффективно использовать мультипарадигменные языки, такие как F#, Scala, Elixir?

B.Meyer утверждает, что OOP и FP не противопоставляются, а дополняют друг друга, и ключем к достижению этого является принцип CQS. В следующих сообщениях приводится его пояснение.

#FunctionalProgramming #OOP #SoftwareDesign
emacsway-log: Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, XP, Agile, etc.
В последнее время наметилась определенная поляризация парадигм программирования в индустрии. Стремительный рост объема обрабатываемых данных, потребность в масштабировании, распределенном хранении и параллельной обработке данных, пробудили интерес к функциональному…
- В последнее время наметилась тенденция в популяризации функциональных языков и функциональной парадигмы программирования. Что вы скажите, является ли объектная технология конкурентом функциональному программированию?

- Нет, эти две парадигмы не являются конкурентами, они успешно могут дополнять друг друга. Тем не менее, тенденция к функциональному программированию является важной и интересной.

На мой взгляд, когда речь идет о высокоуровневой структуре приложения (особенно больших программ), то в мире нет ничего лучше объектного подхода. Я просто не вижу, как можно писать действительно большую программу исключительно на функциональном языке.

С другой стороны, если общая структура приложения построена на основе объектов, то очень даже полезно, если некоторые ее части будут написаны на функциональном языке, для обеспечения простоты и возможности доказательства корректности, о которых я говорил ранее.

Несколько лет назад я опубликовал статью на эту тему, где сравнивал ОО и ФП подходы. В ней я постарался показать, что ОО метод включает функциональное программирование, а не наоборот.

- Да, я кажется читал эту статью, которая затем вошла в качестве одной из глав в книгу “Beautiful Architecture”.

- Вы знаете об этом? Я очень впечатлен.

- (Смеюсь...) Да, и насколько я помню, это был ваш ответ на статью Саймона Пейтона Джонса, в которой автор старался показать, что ФП подход является более предпочтительным.

- Да, совершенно верно.

ПРИМЕЧАНИЕ: Речь идет о статье Бертрана “Software Architecture: Functional vs. Object-Oriented Design in Beautiful Architecture”
http://se.ethz.ch/~meyer/publications/functional/meyer_functional_oo.pdf
, опубликованной в книге “Идеальная архитектура. Ведущие специалисты о красоте программных архитектур.”
https://www.amazon.com/Beautiful-Architecture-Leading-Thinkers-Software/dp/059651798X
. Эта статья Мейера была ответом на статью Саймона “Composing contracts: an adventure in financial engineering.”

- Давайте все же немного вернемся к вопросу OOP vs FP. Какие именно преимущества у функционального подхода на “низком уровне”?

- В Eiffel существует очень важный принцип, под названием Command-Query Separation Principle, который можно рассматривать, в некотором роде, как сближение ОО и ФП миров. Я не считаю, что наличие состояния – это однозначно плохо. Но очень важно, чтобы мы могли ясно различать операции, которые это состояние изменяют (т.е. командами), и операции, которые лишь возвращают информацию о состоянии, его не изменяя (т.е. запросами). В других языках эта разница отсутствует. Так, например, в С/С++ часто пишут функции, которые возвращают результат и изменяют состояние. Следование этому принципу позволяет безопасно использовать выражения с запросами зная, что они не изменяют состояние. В некоторых случаях можно пойти еще дальше и работать в чисто функциональном мире с полным отсутствием побочных эффектов."

- Bertrand Meyer в интервью Сергея Теплякова “Интервью с Бертраном Мейером“
https://sergeyteplyakov.blogspot.com/2014/05/interview-with-bertrand-meyer.html

#FunctionalProgramming #OOP #SoftwareDesign
emacsway-log: Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, XP, Agile, etc.
- В последнее время наметилась тенденция в популяризации функциональных языков и функциональной парадигмы программирования. Что вы скажите, является ли объектная технология конкурентом функциональному программированию? - Нет, эти две парадигмы не являются…
📝 "For both theoretical and practical reasons detailed elsewhere [10], the command-query separation principle is a methodological rule, not a language feature, but all serious software developed in Eiffel observes it scrupulously, to great referential transparency advantage. Although other schools of object-oriented programming regrettable do not apply it (continuing instead the C style of calling functions rather than procedures to achieve changes), but in my view it is a key element of the object-oriented approach. It seems like a viable way to obtain the referential transparency goal of functional programming — since expressions, which only involve queries, will not change the state, and hence can be understood as in traditional mathematics or a functional language — while acknowledging, through the notion of command, the fundamental role of the concept of state in modeling systems and computations."

- “Software architecture: object-oriented vs functional” by Bertrand Meyer http://se.ethz.ch/~meyer/publications/functional/meyer_functional_oo.pdf
emacsway-log: Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, XP, Agile, etc.
Две известные статьи от Rober Martin на тему OOP vs FP: - http://blog.cleancoder.com/uncle-bob/2014/11/24/FPvsOO.html - https://blog.cleancoder.com/uncle-bob/2018/04/13/FPvsOO.html Ну а я, как поклонник Emacs и Lisp, не могу обойти вниманием его статью про…
Кстати, CQS - довольно обширная тема с довольно тонкими нюансами. Его невозможно познать, прочитав, например, статью в википедии. Только первоисточник - "Object-Oriented Software Construction" 2nd edition by Bertrand Meyer.

Я читал про CQS в многочисленных статьях и книгах, но когда я прочел о нем в первоисточнике, то я понял, что совершенно ничего о нем не знал.

#FunctionalProgramming #OOP #SoftwareDesign
Forwarded from Andrei Gordienkov
не знаю как к вам всем обратиться, но хочу поделиться успехом, что моя команда, а по большей части я, победили в том архитектурном конкурсе от O`Reilly
участвовало 100+ команд, и надо бало предаставить реальное решение. В финал вышли 10 команд. Мы победили.
https://github.com/ldynia/archcolider
для рассмотрения и отзывав
Forwarded from Andrei Gordienkov
завтра-послезавтра могу прислать ссылки на другие решения из финала
emacsway-log: Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, XP, Agile, etc.
Кстати, CQS - довольно обширная тема с довольно тонкими нюансами. Его невозможно познать, прочитав, например, статью в википедии. Только первоисточник - "Object-Oriented Software Construction" 2nd edition by Bertrand Meyer. Я читал про CQS в многочисленных…
Поговорим немного о довольно дискуссионном вопросе, может ли CQRS-команда возвращать результат. CQRS лишь немного отличается от CQS по исполнению. Ввел этот термин Greg Young, поэтому, к нему и обратимся:

📝 "Starting with CQRS, CQRS is simply the creation of two objects where there [CQS] was previously only one. The separation occurs based upon whether the methods are a command or a query (the same definition that is used by Meyer in Command and Query Separation, a command is any method that mutates state and a query is any method that returns a value)... That is it. That is the entirety of the CQRS pattern. There is nothing more to it than that…"
- "CQRS, Task Based UIs, Event Sourcing agh!" by Greg Young
http://codebetter.com/gregyoung/2010/02/16/cqrs-task-based-uis-event-sourcing-agh/

📝 "Command and Query Responsibility Segregation was originally considered just to be an extension of this [CQS] concept."
📝 "Command and Query Responsibility Segregation (CQRS) originated with Bertrand Meyer’s Command and Query Separation Principle."
📝 "Command and Query Responsibility Segregation uses the same definition of Commands and Queries that Meyer used and maintains the viewpoint that they should be pure. The fundamental difference is that in CQRS objects are split into two objects, one containing the Commands one containing the
Queries."

- "CQRS Documents by Greg Young"
https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf

#DDD #Microservices #SoftwareDesign #SoftwareArchitecture #FunctionalProgramming #OOP
emacsway-log: Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, XP, Agile, etc.
Поговорим немного о довольно дискуссионном вопросе, может ли CQRS-команда возвращать результат. CQRS лишь немного отличается от CQS по исполнению. Ввел этот термин Greg Young, поэтому, к нему и обратимся: 📝 "Starting with CQRS, CQRS is simply the creation…
В одном из самых авторитетных reference application eShopOnContainers от Microsoft, одна из CQRS-команд возвращает результат:

- https://github.com/dotnet-architecture/eShopOnContainers/blob/b1021c88d55d96c247eab75bde650ab4b194f706/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommandHandler.cs#L40

- https://github.com/dotnet-architecture/eShopOnContainers/blob/b1021c88d55d96c247eab75bde650ab4b194f706/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs#L151

Однако, в известной "Красной книге", Vaughn Vernon пишет:

📝 "This principle, devised by Bertrand Meyer, asserts the following:

"Every method should be either a command that performs an action, or a query that returns data to the caller, but not both. In other words, asking a question should not change the answer.More formally, methods should return a value only if they are referentially transparent and hence possess no side effects." [Wikipedia, CQS]

At an object level this means:

1. If a method modifies the state of the object, it is a command, and its method must not return a value. In Java and C# the method must be declared void .

2. If a method returns some value, it is a query, and it must not directly or indirectly cause the modification of the state of the object. In Java and C# the method must be declared with the type of the value it returns."


- "Implementing Domain-Driven Design" by Vaughn Vernon, Chapter "4. Architecture :: Command-Query Responsibility Segregation, or CQRS"

Другое, не менее авторитетное архитектурное руководство от Microsoft, утверждает:

📝 "A query returns data and does not alter the state of the object; a command changes the state of an object but does not return any data."
- "CQRS Journey :: Reference 2: Introducing the Command Query Responsibility Segregation Pattern :: What is CQRS?"
https://docs.microsoft.com/en-us/previous-versions/msp-n-p/jj591573(v=pandp.10)#what-is-cqrs

Противоречие? Архитектура - это, как известно, наука об ограничениях, о том, как не надо делать. Почему же тогда одно из самых авторитетных reference application, консультантами которого являются такие светила, как Cesar De la Torre, Jimmy Nilsson, Udi Dahan, Jimmy Bogard, и другие, это ограничение нарушает? Что это - компромисс, вызванный практической целесообразностью, или демонстрация принципиального архитектурно чистого решения?

Ответ на этот вопрос мы попытаемся найти в следующих постах.

#DDD #Microservices #SoftwareDesign #SoftwareArchitecture #FunctionalProgramming #OOP
emacsway-log: Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, XP, Agile, etc.
В одном из самых авторитетных reference application eShopOnContainers от Microsoft, одна из CQRS-команд возвращает результат: - https://github.com/dotnet-architecture/eShopOnContainers/blob/b1021c88d55d96c247eab75bde650ab4b194f706/src/Services/Ordering/O…
Итак, начнем по порядку, ибо тему в один пост не впихнуть. Начнем с принципа CQS:

📝 "Command-Query Separation principle - Functions should not produce abstract side effects."
- "Object-Oriented Software Construction" 2nd edition by Bertrand Meyer, chapter "23.1 SIDE EFFECTS IN FUNCTIONS"

Обратите внимание на термин abstract. B.Meyer различает abstract и concrete side effects.

📝 "Definition: concrete side effect: A function produces a concrete side effect if its body contains any of the following:
1. An assignment, assignment attempt or creation instruction whose target is an attribute.
2. A procedure call."
(там же)

📝 "Since not every class definition is accompanied by a full-fledged specification of the underlying abstract data type, we need a more directly usable definition of “abstract side effect”. This is not difficult. In practice, the abstract data type is defined by the interface offered by a class to its clients (expressed for example as the short form of the class). A side effect will affect the abstract object if it changes the result of any query accessible to these clients. Hence the definition:

Definition: abstract side effect: An abstract side effect is a concrete side effect that can change the value of a non-secret query.

The definition refers to “non-secret” rather than exported queries. The reason is that in-between generally exported and fully secret status, we must permit a query to be selectively exported to a set of clients. As soon as a query is non-secret — exported to any client other than NONE — we consider that changing its result is an abstract side effect, since the change will be visible to at least some clients." (там же)

#DDD #Microservices #SoftwareDesign #SoftwareArchitecture #FunctionalProgramming #OOP
emacsway-log: Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, XP, Agile, etc.
Итак, начнем по порядку, ибо тему в один пост не впихнуть. Начнем с принципа CQS: 📝 "Command-Query Separation principle - Functions should not produce abstract side effects." - "Object-Oriented Software Construction" 2nd edition by Bertrand Meyer, chapter…
📝 "The Command-Query Separation principle brings referential transparency back." (там же)

📝 "Definition: referential transparency: An expression e is referentially transparent if it is possible to exchange any subexpression with its value without changing the value of e." (там же)

Подведу короткое резюме всему ранее сказанному: CQS не запрещает изменять состояние, если оно не нарушает ссылочную прозрачность. Соблюдение этого условия открывает нам возможность пользоваться всеми преимуществами функционального программирования. Это и есть цель CQS.

Не Команде запрещено возвращать информацию об объекте, а Запросу на получение информации об объекте запрещено нарушать ссылочную прозрачность.

На это указывает и сам B. Meyer (учтите, что Railway Oriented Programming ( https://fsharpforfunandprofit.com/rop/ ) и Result type ( https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/results ) в то время еще не было):

📝 "The first has to do with error handling. Sometimes a function with side effects is really a procedure, which in addition to doing its job returns a status code indicating how things went. But there are better ways to do this; roughly speaking, the proper O-O technique is to enable the client, after an operation on an object, to perform a query on the status, represented for example by an attribute of the object, as in

target.some_operation(…)
how_did_it_go := targetGstatus

Note that the technique of returning a status as function result is lame anyway. It transforms a procedure into a function by adding the status as a result; but it does not work if the routine was already a function, which already has a result of its own. It is also problematic if you need more than one status indicator. In such cases the C approach is either to return a “structure” (the equivalent of an object) with several components, which is getting close to the above scheme, or to use global variables — which raises a whole set of new problems, especially in a large system where many modules can trigger errors." (там же)

Таким образом, строгого запрета на возврат командой чего-либо (например, информации об ошибке выполнения) не существует. Существует только пояснение почему и в пользу чего нужно стремиться этого избегать, где основной причиной для избегания является как раз именно то, что команда может возвращать значение, отличное от информации об ошибке.

#DDD #Microservices #SoftwareDesign #SoftwareArchitecture #FunctionalProgramming #OOP
emacsway-log: Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, XP, Agile, etc.
📝 "The Command-Query Separation principle brings referential transparency back." (там же) 📝 "Definition: referential transparency: An expression e is referentially transparent if it is possible to exchange any subexpression with its value without changing…
Таким образом, мы выяснили, что команда может быть функцией, возвращающей служебную информацию об успешности выполнения, если иной способ невозможен. Вернемся к основам:

📝 "Commands and queries.

A few reminders on terminology will be useful. The features that characterize a class are divided into commands and queries. A command serves to modify objects, a query to return information about objects. A command is implemented as a procedure. A query may be implemented either as an attribute, that is to say by reserving a field in each run-time instance of the class to hold the corresponding value, or as a function, that is to say through an algorithm that computes the value when needed. Procedures (which also have an associated algorithm) and functions are together called routines.

The definition of queries does not specify whether in the course of producing its result a query may change objects. For commands, the answer is obviously yes, since it is the role of commands (procedures) to change things. Among queries, the question only makes sense for functions, since accessing an attribute cannot change anything. A change performed by a function is known as a side effect to indicate that it is ancillary to the function’s official purpose of answering a query. Should we permit side effects?" (там же)

Отсюда следует ряд выводов. Основной вопрос CQS лежит в плоскости Queries, и сводится с ссылочной прозрачности.

Хотя B.Meyer и использует термин procedure, которая, по определению ничего не возвращает ("Procedure
A routine which does not return a result. (The other form of routine is the function.)" - glossary книги), он ясно выразил разделение Команд и Запросов по назначению: "A command serves to modify objects, a query to return information about objects."

Это определение не отвечает на вопрос, изменится ли суть команды, если она будет возвращать служебную информацию о процессе выполнения, которая не является информацией об объекте, и не нарушает ссылочную прозрачность (которая по определению не применима к командам). Этот момент очень важен, и в будущем мы еще к нему вернемся. Но, зато, он ясно дал понять, что команда может возвращать значение, и именно поэтому, желательно избегать возврата ею информации об ошибке. В наши дни, напомню, такая проблема больше не актуальна. Тем более, она не актуальна при переносе этого вопроса на способы сетевого взаимодействия.

#DDD #Microservices #SoftwareDesign #SoftwareArchitecture #FunctionalProgramming #OOP
emacsway-log: Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, XP, Agile, etc.
Таким образом, мы выяснили, что команда может быть функцией, возвращающей служебную информацию об успешности выполнения, если иной способ невозможен. Вернемся к основам: 📝 "Commands and queries. A few reminders on terminology will be useful. The features…
А теперь самое важное. При обсуждении CQRS этот момент часто незаслуженно опускается. Кроме процедур-команд и функций-запросов, Bertrand Meyer вводит еще и функции-конструкторы! И вот тут кроется интересное:

📝 "Functions that create objects.

A technical point needs to be clarified before we examine further consequences of the Command-Query Separation principle: should we treat object creation as a side effect?

The answer is yes, as we have seen, if the target of the creation is an attribute a: in this case, the instruction !! a changes the value of an object’s field. The answer is no if the target is a local entity of the routine. But what if the target is the result of the function itself, as in !! Result or the more general form !! Result.make (…)?

Such a creation instruction need not be considered a side effect. It does not change any existing object and so does not endanger referential transparency (at least if we assume that there is enough memory to allocate all the objects we need).

From a mathematical perspective we may pretend that all of the objects of interest, for all times past, present and future, are already inscribed in the Great Book of Objects; a creation instruction is just a way to obtain one of them, but it does not by itself change anything in the environment. It is common, and legitimate, for a function to create, initialize and return such an object.

These observations assume that in the second form the creation procedure make does not produce side effects on any object other than the one being created." (там же)

#DDD #Microservices #SoftwareDesign #SoftwareArchitecture #FunctionalProgramming #OOP
emacsway-log: Software Design, Clean Architecture, DDD, Microservice Architecture, Distributed Systems, XP, Agile, etc.
А теперь самое важное. При обсуждении CQRS этот момент часто незаслуженно опускается. Кроме процедур-команд и функций-запросов, Bertrand Meyer вводит еще и функции-конструкторы! И вот тут кроется интересное: 📝 "Functions that create objects. A technical…
Это замечание B.Meyer является очень важным, так как наиболее частый вопрос CQRS - это возврат идентификатора созданного ресурса и исполнение требований RFC-7231 для HTTP-method POST REST API:

📝 "the origin server SHOULD send a 201 (Created) response containing a Location header field that provides an identifier for the primary resource created (Section 7.1.2) and a representation that describes the status of the request while referring to the new resource(s).
- "Section 4.3.3. POST of RFC-7231"
https://tools.ietf.org/html/rfc7231#section-4.3.3

#DDD #Microservices #SoftwareDesign #SoftwareArchitecture #FunctionalProgramming #OOP