[{"data":1,"prerenderedAt":17377},["ShallowReactive",2],{"navigation_docs":3,"-tools-docker-docker-compose-basics":3099,"-tools-docker-docker-compose-basics-surround":17372},[4,1657,1810,2264,2445,2652,2774,2824,2881,2915,3041,3058,3095],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"C#","i-devicon-csharp","/csharp","01.csharp",[10,13,60,90,120,202,219,253,379,404,457,650,1346,1636,1653],{"title":11,"path":7,"stem":12},"C# та .NET","01.csharp/index",{"title":14,"icon":15,"path":16,"stem":17,"children":18,"page":59},"Fundamentals","i-lucide-book-open","/csharp/fundamentals","01.csharp/01.fundamentals",[19,23,27,31,35,39,43,47,51,55],{"title":20,"path":21,"stem":22},"Вступ до екосистеми .NET","/csharp/fundamentals/introduction-to-ecosystem","01.csharp/01.fundamentals/01.introduction-to-ecosystem",{"title":24,"path":25,"stem":26},"Структура програми на C#","/csharp/fundamentals/program-structure","01.csharp/01.fundamentals/02.program-structure",{"title":28,"path":29,"stem":30},"Змінні та Типи Даних","/csharp/fundamentals/variables-data-types","01.csharp/01.fundamentals/03.variables-data-types",{"title":32,"path":33,"stem":34},"Масиви","/csharp/fundamentals/arrays","01.csharp/01.fundamentals/04.arrays",{"title":36,"path":37,"stem":38},"Strings & Text Handling","/csharp/fundamentals/strings-text-handling","01.csharp/01.fundamentals/05.strings-text-handling",{"title":40,"path":41,"stem":42},"Дати і Час","/csharp/fundamentals/dates-time-handling","01.csharp/01.fundamentals/06.dates-time-handling",{"title":44,"path":45,"stem":46},"Потік Керування","/csharp/fundamentals/control-flow","01.csharp/01.fundamentals/07.control-flow",{"title":48,"path":49,"stem":50},"Методи","/csharp/fundamentals/methods","01.csharp/01.fundamentals/08.methods",{"title":52,"path":53,"stem":54},"Основи Відлагодження","/csharp/fundamentals/debugging-basics","01.csharp/01.fundamentals/09.debugging-basics",{"title":56,"path":57,"stem":58},"Інтерактивна Консоль (Classic)","/csharp/fundamentals/interactive-console","01.csharp/01.fundamentals/10.interactive-console",false,{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":59},"OOP","i-lucide-box","/csharp/oop","01.csharp/02.oop",[66,70,74,78,82,86],{"title":67,"path":68,"stem":69},"Package Management (Управління Пакетами)","/csharp/oop/package-management","01.csharp/02.oop/01.package-management",{"title":71,"path":72,"stem":73},"Класи та Об'єкти","/csharp/oop/classes-objects","01.csharp/02.oop/02.classes-objects",{"title":75,"path":76,"stem":77},"Властивості та Поля","/csharp/oop/properties-fields","01.csharp/02.oop/03.properties-fields",{"title":79,"path":80,"stem":81},"Стовпи ООП","/csharp/oop/oop-pillars","01.csharp/02.oop/04.oop-pillars",{"title":83,"path":84,"stem":85},"Advanced Types","/csharp/oop/advanced-types","01.csharp/02.oop/05.advanced-types",{"title":87,"path":88,"stem":89},"Namespaces (Простори Імен)","/csharp/oop/namespaces","01.csharp/02.oop/06.namespaces",{"title":91,"icon":92,"path":93,"stem":94,"children":95,"page":59},"Advanced Core","i-lucide-zap","/csharp/advanced-core","01.csharp/03.advanced-core",[96,100,104,108,112,116],{"title":97,"path":98,"stem":99},"Generics (Узагальнення)","/csharp/advanced-core/generics","01.csharp/03.advanced-core/01.generics",{"title":101,"path":102,"stem":103},"Делегати, Події та Лямбда-вирази","/csharp/advanced-core/delegates-events-lambdas","01.csharp/03.advanced-core/02.delegates-events-lambdas",{"title":105,"path":106,"stem":107},"Interfaces Deep Dive (Інтерфейси: Поглиблений Розгляд)","/csharp/advanced-core/interfaces-deep-dive","01.csharp/03.advanced-core/03.interfaces-deep-dive",{"title":109,"path":110,"stem":111},"Обробка Винятків","/csharp/advanced-core/exception-handling","01.csharp/03.advanced-core/04.exception-handling",{"title":113,"path":114,"stem":115},"Pattern Matching","/csharp/advanced-core/pattern-matching","01.csharp/03.advanced-core/05.pattern-matching",{"title":117,"path":118,"stem":119},"Додаткові Можливості C#","/csharp/advanced-core/additional-features","01.csharp/03.advanced-core/06.additional-features",{"title":121,"icon":122,"path":123,"stem":124,"children":125,"page":59},"Architecture Best Practices","i-lucide-building-2","/csharp/architecture-best-practices","01.csharp/04.architecture-best-practices",[126,130,149,153,157,161,165,169],{"title":127,"path":128,"stem":129},"Software Design Principles (Частина 1)","/csharp/architecture-best-practices/software-design-principles","01.csharp/04.architecture-best-practices/01.software-design-principles",{"title":131,"icon":132,"path":133,"stem":134,"children":135,"page":59},"Design Patterns","i-lucide-folder","/csharp/architecture-best-practices/design-patterns","01.csharp/04.architecture-best-practices/02.design-patterns",[136],{"title":137,"icon":132,"path":138,"stem":139,"children":140,"page":59},"Creational","/csharp/architecture-best-practices/design-patterns/creational","01.csharp/04.architecture-best-practices/02.design-patterns/creational",[141,145],{"title":142,"path":143,"stem":144},"Singleton (Одинак)","/csharp/architecture-best-practices/design-patterns/creational/singleton","01.csharp/04.architecture-best-practices/02.design-patterns/creational/01.singleton",{"title":146,"path":147,"stem":148},"Builder (Будівельник)","/csharp/architecture-best-practices/design-patterns/creational/builder","01.csharp/04.architecture-best-practices/02.design-patterns/creational/02.builder",{"title":150,"path":151,"stem":152},"Building Professional CLIs","/csharp/architecture-best-practices/building-professional-clis","01.csharp/04.architecture-best-practices/03.building-professional-clis",{"title":154,"path":155,"stem":156},"Validation & Flow Control","/csharp/architecture-best-practices/validation-flow-control","01.csharp/04.architecture-best-practices/04.validation-flow-control",{"title":158,"path":159,"stem":160},"The Modern .NET Host (Microsoft.Extensions)","/csharp/architecture-best-practices/modern-dotnet-host","01.csharp/04.architecture-best-practices/05.modern-dotnet-host",{"title":162,"path":163,"stem":164},"Data Mapper: Repository та DAO патерни (Частина 1)","/csharp/architecture-best-practices/data-mapper-part1","01.csharp/04.architecture-best-practices/06.data-mapper-part1",{"title":166,"path":167,"stem":168},"Data Mapper: Repository та DAO патерни (Частина 2)","/csharp/architecture-best-practices/data-mapper-part2","01.csharp/04.architecture-best-practices/07.data-mapper-part2",{"title":170,"icon":132,"path":171,"stem":172,"children":173,"page":59},"Di Ioc","/csharp/architecture-best-practices/di-ioc","01.csharp/04.architecture-best-practices/08.di-ioc",[174,178,182,186,190,194,198],{"title":175,"path":176,"stem":177},"Проблема залежностей та Інверсія Контролю","/csharp/architecture-best-practices/di-ioc/the-dependency-problem","01.csharp/04.architecture-best-practices/08.di-ioc/01.the-dependency-problem",{"title":179,"path":180,"stem":181},"Будуємо власний Service Container","/csharp/architecture-best-practices/di-ioc/build-your-own-container","01.csharp/04.architecture-best-practices/08.di-ioc/02.build-your-own-container",{"title":183,"path":184,"stem":185},"Service Locator: Паттерн та Анти-паттерн","/csharp/architecture-best-practices/di-ioc/service-locator-pattern","01.csharp/04.architecture-best-practices/08.di-ioc/03.service-locator-pattern",{"title":187,"path":188,"stem":189},"Паттерни Dependency Injection","/csharp/architecture-best-practices/di-ioc/dependency-injection-patterns","01.csharp/04.architecture-best-practices/08.di-ioc/04.dependency-injection-patterns",{"title":191,"path":192,"stem":193},"Microsoft DI: IServiceCollection та IServiceProvider","/csharp/architecture-best-practices/di-ioc/microsoft-di-deep-dive","01.csharp/04.architecture-best-practices/08.di-ioc/05.microsoft-di-deep-dive",{"title":195,"path":196,"stem":197},"Service Lifetimes та Scopes","/csharp/architecture-best-practices/di-ioc/service-lifetimes-and-scopes","01.csharp/04.architecture-best-practices/08.di-ioc/06.service-lifetimes-and-scopes",{"title":199,"path":200,"stem":201},"DI Анти-паттерни та Найкращі Практики","/csharp/architecture-best-practices/di-ioc/di-anti-patterns-and-best-practices","01.csharp/04.architecture-best-practices/08.di-ioc/07.di-anti-patterns-and-best-practices",{"title":203,"icon":132,"path":204,"stem":205,"children":206,"page":59},"Standard Library","/csharp/standard-library","01.csharp/05.standard-library",[207,211,215],{"title":208,"path":209,"stem":210},"Collections (Колекції)","/csharp/standard-library/collections","01.csharp/05.standard-library/01.collections",{"title":212,"path":213,"stem":214},"High Performance Types (Високопродуктивні Типи)","/csharp/standard-library/high-performance-types","01.csharp/05.standard-library/02.high-performance-types",{"title":216,"path":217,"stem":218},"LINQ (Language Integrated Query)","/csharp/standard-library/linq","01.csharp/05.standard-library/03.linq",{"title":220,"icon":221,"path":222,"stem":223,"children":224,"page":59},"System Internals Concurrency","i-lucide-server","/csharp/system-internals-concurrency","01.csharp/06.system-internals-concurrency",[225,229,233,237,241,245,249],{"title":226,"path":227,"stem":228},"Memory Management","/csharp/system-internals-concurrency/memory-management","01.csharp/06.system-internals-concurrency/01.memory-management",{"title":230,"path":231,"stem":232},"Reflection API: System.Type та Метадані","/csharp/system-internals-concurrency/reflection-fundamentals","01.csharp/06.system-internals-concurrency/02.reflection-fundamentals",{"title":234,"path":235,"stem":236},"Attributes та Dynamic Language Runtime","/csharp/system-internals-concurrency/attributes-dynamic","01.csharp/06.system-internals-concurrency/03.attributes-dynamic",{"title":238,"path":239,"stem":240},"Expression Trees: Швидка Альтернатива Рефлексії","/csharp/system-internals-concurrency/expression-trees-compiled","01.csharp/06.system-internals-concurrency/04.expression-trees-compiled",{"title":242,"path":243,"stem":244},"Source Generators: Compile-Time Code Generation","/csharp/system-internals-concurrency/source-generators","01.csharp/06.system-internals-concurrency/05.source-generators",{"title":246,"path":247,"stem":248},"Multithreading Fundamentals","/csharp/system-internals-concurrency/multithreading-fundamentals","01.csharp/06.system-internals-concurrency/06.multithreading-fundamentals",{"title":250,"path":251,"stem":252},"Synchronization Primitives","/csharp/system-internals-concurrency/synchronization-primitives","01.csharp/06.system-internals-concurrency/07.synchronization-primitives",{"title":254,"icon":255,"path":256,"stem":257,"children":258,"page":59},"System Programming Windows","i-lucide-cpu","/csharp/system-programming-windows","01.csharp/07.system-programming-windows",[259,263,267,271,275,279,283,287,291,295,299,303,307,311,315,319,323,327,331,335,339,343,347,351,355,359,363,367,371,375],{"title":260,"path":261,"stem":262},"Як Працює Операційна Система","/csharp/system-programming-windows/how-os-works","01.csharp/07.system-programming-windows/01.how-os-works",{"title":264,"path":265,"stem":266},"Процеси в .NET — API та Запуск","/csharp/system-programming-windows/processes-in-dotnet","01.csharp/07.system-programming-windows/02.processes-in-dotnet",{"title":268,"path":269,"stem":270},"Процеси в .NET — IPC та Міжпроцесна Комунікація","/csharp/system-programming-windows/02a.processes-ipc","01.csharp/07.system-programming-windows/02a.processes-ipc",{"title":272,"path":273,"stem":274},"Application Domains та Збірки — AppDomain і AssemblyLoadContext","/csharp/system-programming-windows/appdomains-assemblies","01.csharp/07.system-programming-windows/03.appdomains-assemblies",{"title":276,"path":277,"stem":278},"Application Domains та Збірки — Plug-in Система з Hot-Reload","/csharp/system-programming-windows/03a.appdomains-plugin-system","01.csharp/07.system-programming-windows/03a.appdomains-plugin-system",{"title":280,"path":281,"stem":282},"Потоки — Основи та API Thread","/csharp/system-programming-windows/thread-fundamentals","01.csharp/07.system-programming-windows/04.thread-fundamentals",{"title":284,"path":285,"stem":286},"Потоки — Lifecycle, Пріоритети та Безпечне Завершення","/csharp/system-programming-windows/04a.thread-lifecycle-priorities","01.csharp/07.system-programming-windows/04a.thread-lifecycle-priorities",{"title":288,"path":289,"stem":290},"Проблеми Спільного Стану — Race Condition та Data Race","/csharp/system-programming-windows/shared-state-problems","01.csharp/07.system-programming-windows/05.shared-state-problems",{"title":292,"path":293,"stem":294},"Проблеми Спільного Стану — Memory Model та volatile","/csharp/system-programming-windows/05a.shared-state-memory-model","01.csharp/07.system-programming-windows/05a.shared-state-memory-model",{"title":296,"path":297,"stem":298},"Синхронізація — Monitor, lock та еволюція примітивів","/csharp/system-programming-windows/synchronization-fundamentals","01.csharp/07.system-programming-windows/06.synchronization-fundamentals",{"title":300,"path":301,"stem":302},"Синхронізація — Наскрізний Приклад та Deadlock Detection","/csharp/system-programming-windows/06a.synchronization-walkthrough","01.csharp/07.system-programming-windows/06a.synchronization-walkthrough",{"title":304,"path":305,"stem":306},"Синхронізація — Mutex, Semaphore та Event-Based Primitives","/csharp/system-programming-windows/synchronization-advanced","01.csharp/07.system-programming-windows/07.synchronization-advanced",{"title":308,"path":309,"stem":310},"Синхронізація — Interlocked, Volatile та Lock-Free Структури","/csharp/system-programming-windows/07a.synchronization-advanced-walkthrough","01.csharp/07.system-programming-windows/07a.synchronization-advanced-walkthrough",{"title":312,"path":313,"stem":314},"Interlocked, CAS та Lock-Free Структури","/csharp/system-programming-windows/interlocked-cas-lockfree","01.csharp/07.system-programming-windows/08.interlocked-cas-lockfree",{"title":316,"path":317,"stem":318},"Volatile, Memory Model та Spinning","/csharp/system-programming-windows/08a.volatile-memory-model","01.csharp/07.system-programming-windows/08a.volatile-memory-model",{"title":320,"path":321,"stem":322},"ThreadPool — Пул Потоків для Ефективного Виконання","/csharp/system-programming-windows/thread-pool","01.csharp/07.system-programming-windows/09.thread-pool",{"title":324,"path":325,"stem":326},"ThreadPool — Просунуті Сценарії та Внутрішня Будова","/csharp/system-programming-windows/09a.thread-pool-advanced","01.csharp/07.system-programming-windows/09a.thread-pool-advanced",{"title":328,"path":329,"stem":330},"Concurrent та Immutable Collections","/csharp/system-programming-windows/concurrent-collections","01.csharp/07.system-programming-windows/10.concurrent-collections",{"title":332,"path":333,"stem":334},"TPL, Task та Композиція — Від Thread до Task","/csharp/system-programming-windows/tpl-parallel-plinq","01.csharp/07.system-programming-windows/11.tpl-parallel-plinq",{"title":336,"path":337,"stem":338},"Parallel Class та PLINQ — Data Parallelism","/csharp/system-programming-windows/11a.tpl-parallel-plinq-advanced","01.csharp/07.system-programming-windows/11a.tpl-parallel-plinq-advanced",{"title":340,"path":341,"stem":342},"Async/Await — Фундамент Асинхронного Програмування","/csharp/system-programming-windows/async-fundamentals","01.csharp/07.system-programming-windows/12.async-fundamentals",{"title":344,"path":345,"stem":346},"SynchronizationContext та ConfigureAwait — Контекст Виконання","/csharp/system-programming-windows/async-context-configureawait","01.csharp/07.system-programming-windows/13.async-context-configureawait",{"title":348,"path":349,"stem":350},"Async — Просунуті Паттерни","/csharp/system-programming-windows/async-advanced","01.csharp/07.system-programming-windows/14.async-advanced",{"title":352,"path":353,"stem":354},"System.Threading.Channels — Async Producer-Consumer","/csharp/system-programming-windows/channels","01.csharp/07.system-programming-windows/15.channels",{"title":356,"path":357,"stem":358},"Асинхронна Синхронізація","/csharp/system-programming-windows/async-synchronization","01.csharp/07.system-programming-windows/16.async-synchronization",{"title":360,"path":361,"stem":362},"Unsafe Code та Вказівники","/csharp/system-programming-windows/unsafe-code","01.csharp/07.system-programming-windows/17.unsafe-code",{"title":364,"path":365,"stem":366},"P/Invoke та Windows API — Міст між .NET та Native Code","/csharp/system-programming-windows/pinvoke-winapi","01.csharp/07.system-programming-windows/18.pinvoke-winapi",{"title":368,"path":369,"stem":370},"Реєстр Windows — Центральна База Конфігурації Системи","/csharp/system-programming-windows/windows-registry","01.csharp/07.system-programming-windows/19.windows-registry",{"title":372,"path":373,"stem":374},"Windows Hooks, Hotkeys та Services — Глибока Інтеграція з ОС","/csharp/system-programming-windows/windows-hooks-services","01.csharp/07.system-programming-windows/20.windows-hooks-services",{"title":376,"path":377,"stem":378},"Системне Програмування C# (Windows) — 07.system-programming-windows","/csharp/system-programming-windows/implementation_plan","01.csharp/07.system-programming-windows/implementation_plan",{"title":380,"icon":132,"path":381,"stem":382,"children":383,"page":59},"Io","/csharp/io","01.csharp/08.io",[384,388,392,396,400],{"title":385,"path":386,"stem":387},"8.1.1. Основи роботи з файловою системою","/csharp/io/file-system-basics","01.csharp/08.io/01.file-system-basics",{"title":389,"path":390,"stem":391},"8.1.2. Потоки (Streams) та Серіалізація Даних","/csharp/io/streams-serialization","01.csharp/08.io/02.streams-serialization",{"title":393,"path":394,"stem":395},"8.2.1. JSON Serialization з System.Text.Json","/csharp/io/json-serialization","01.csharp/08.io/03.json-serialization",{"title":397,"path":398,"stem":399},"8.2.2. XML Serialization та LINQ to XML","/csharp/io/xml-serialization","01.csharp/08.io/04.xml-serialization",{"title":401,"path":402,"stem":403},"8.2.3. Binary Serialization: MessagePack та Protocol Buffers","/csharp/io/binary-serialization","01.csharp/08.io/05.binary-serialization",{"title":405,"icon":132,"path":406,"stem":407,"children":408,"page":59},"Ado Net","/csharp/ado-net","01.csharp/09.ado-net",[409,413,417,421,425,429,433,437,441,445,449,453],{"title":410,"path":411,"stem":412},"9.1. Введення в ADO.NET","/csharp/ado-net/introduction-to-adonet","01.csharp/09.ado-net/01.introduction-to-adonet",{"title":414,"path":415,"stem":416},"9.2. Клас DbConnection — з'єднання з базою даних","/csharp/ado-net/connection","01.csharp/09.ado-net/02.connection",{"title":418,"path":419,"stem":420},"9.3. Клас DbCommand — виконання SQL-запитів","/csharp/ado-net/command-and-queries","01.csharp/09.ado-net/03.command-and-queries",{"title":422,"path":423,"stem":424},"9.4. Клас DbDataReader — ефективне читання даних","/csharp/ado-net/datareader","01.csharp/09.ado-net/04.datareader",{"title":426,"path":427,"stem":428},"9.5. Параметризовані запити та захист від SQL Injection","/csharp/ado-net/parameters-and-sql-injection","01.csharp/09.ado-net/05.parameters-and-sql-injection",{"title":430,"path":431,"stem":432},"9.6. Транзакції в ADO.NET","/csharp/ado-net/transactions","01.csharp/09.ado-net/06.transactions",{"title":434,"path":435,"stem":436},"9.7. DbProviderFactory — провайдер-незалежний код","/csharp/ado-net/provider-factory","01.csharp/09.ado-net/07.provider-factory",{"title":438,"path":439,"stem":440},"9.8. Асинхронний доступ до даних","/csharp/ado-net/async-data-access","01.csharp/09.ado-net/08.async-data-access",{"title":442,"path":443,"stem":444},"9.9. Від'єднаний режим: DataSet, DataTable, DataRow","/csharp/ado-net/disconnected-mode-dataset","01.csharp/09.ado-net/09.disconnected-mode-dataset",{"title":446,"path":447,"stem":448},"9.10. DataAdapter — міст між DataSet та базою даних","/csharp/ado-net/data-adapter","01.csharp/09.ado-net/10.data-adapter",{"title":450,"path":451,"stem":452},"9.11. Data Mapper та Repository: Архітектура доступу до даних","/csharp/ado-net/data-mapper-repository","01.csharp/09.ado-net/11.data-mapper-repository",{"title":454,"path":455,"stem":456},"9.12. Identity Map, Unit of Work та Specification Pattern","/csharp/ado-net/advanced-patterns","01.csharp/09.ado-net/12.advanced-patterns",{"title":458,"icon":255,"path":459,"stem":460,"children":461,"page":59},"Ef Core","/csharp/ef-core","01.csharp/10.ef-core",[462,466,470,474,478,482,486,490,494,498,502,506,510,514,518,522,526,532,538,542,546,550,554,558,562,566,570,574,578,582,586,590,594,598,602,606,610,614,618,622,626,630,634,638,642,646],{"title":463,"path":464,"stem":465},"Що таке ORM? Від SQL до об'єктів","/csharp/ef-core/what-is-orm","01.csharp/10.ef-core/01.what-is-orm",{"title":467,"path":468,"stem":469},"Перший проєкт — від нуля до CRUD","/csharp/ef-core/first-project","01.csharp/10.ef-core/02.first-project",{"title":471,"path":472,"stem":473},"DbContext — Серце EF Core","/csharp/ef-core/dbcontext-deep-dive","01.csharp/10.ef-core/03.dbcontext-deep-dive",{"title":475,"path":476,"stem":477},"Провайдери баз даних — Архітектура та Вибір СУБД","/csharp/ef-core/database-providers","01.csharp/10.ef-core/04.database-providers",{"title":479,"path":480,"stem":481},"Конвенції EF Core — Магія без конфігурації","/csharp/ef-core/conventions","01.csharp/10.ef-core/05.conventions",{"title":483,"path":484,"stem":485},"Fluent API та Data Annotations — Явна конфігурація моделі","/csharp/ef-core/fluent-api-vs-annotations","01.csharp/10.ef-core/06.fluent-api-vs-annotations",{"title":487,"path":488,"stem":489},"Зв'язки — One-to-One та One-to-Many","/csharp/ef-core/relationships-basics","01.csharp/10.ef-core/07.relationships-basics",{"title":491,"path":492,"stem":493},"Зв'язки Advanced — Many-to-Many та Складні Сценарії","/csharp/ef-core/relationships-advanced","01.csharp/10.ef-core/08.relationships-advanced",{"title":495,"path":496,"stem":497},"Властивості — Типи, Конвертери, Компаратори (Частина 1)","/csharp/ef-core/property-configuration-part1","01.csharp/10.ef-core/09.property-configuration-part1",{"title":499,"path":500,"stem":501},"Властивості — Value Comparers, Generators, Shadow Properties (Частина 2)","/csharp/ef-core/property-configuration-part2","01.csharp/10.ef-core/09.property-configuration-part2",{"title":503,"path":504,"stem":505},"Складні типи — Owned Types та Complex Types (Частина 1)","/csharp/ef-core/complex-types-owned-part1","01.csharp/10.ef-core/10.complex-types-owned-part1",{"title":507,"path":508,"stem":509},"Складні типи — Complex Types, Keyless Entities, Порівняння (Частина 2)","/csharp/ef-core/complex-types-owned-part2","01.csharp/10.ef-core/10.complex-types-owned-part2",{"title":511,"path":512,"stem":513},"JSON Columns — Складні дані у JSON (Частина 1)","/csharp/ef-core/json-columns-part1","01.csharp/10.ef-core/11.json-columns-part1",{"title":515,"path":516,"stem":517},"JSON Columns — Value Comparers, Індекси, Провайдери (Частина 2)","/csharp/ef-core/json-columns-part2","01.csharp/10.ef-core/11.json-columns-part2",{"title":519,"path":520,"stem":521},"Успадкування — Абстрактні класи та TPH (Частина 1)","/csharp/ef-core/inheritance-part1","01.csharp/10.ef-core/12.inheritance-part1",{"title":523,"path":524,"stem":525},"Успадкування — TPT, TPC та Порівняння Стратегій (Частина 2)","/csharp/ef-core/inheritance-part2","01.csharp/10.ef-core/12.inheritance-part2",{"title":527,"path":528,"stem":529,"children":530},"Індекси, Обмеження та Схема (Частина 1)","/csharp/ef-core/indexes-constraints-part1","01.csharp/10.ef-core/13.indexes-constraints-part1",[531],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"Індекси, Обмеження та Схема (Частина 2)","/csharp/ef-core/indexes-constraints-part2","01.csharp/10.ef-core/13.indexes-constraints-part2",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541},"Seed Data — Початкові Дані (Частина 1)","/csharp/ef-core/seeding-part1","01.csharp/10.ef-core/14.seeding-part1",{"title":543,"path":544,"stem":545},"Seed Data — SQL-скрипти, Bogus та Стратегії (Частина 2)","/csharp/ef-core/seeding-part2","01.csharp/10.ef-core/14.seeding-part2",{"title":547,"path":548,"stem":549},"Global Query Filters — Глобальні Фільтри (Частина 1)","/csharp/ef-core/global-query-filters-part1","01.csharp/10.ef-core/15.global-query-filters-part1",{"title":551,"path":552,"stem":553},"Global Query Filters — Підводні камені та Інтеграція (Частина 2)","/csharp/ef-core/global-query-filters-part2","01.csharp/10.ef-core/15.global-query-filters-part2",{"title":555,"path":556,"stem":557},"LINQ-запити в EF Core (Частина 1)","/csharp/ef-core/linq-queries-part1","01.csharp/10.ef-core/16.linq-queries-part1",{"title":559,"path":560,"stem":561},"LINQ-запити в EF Core (Частина 2)","/csharp/ef-core/linq-queries-part2","01.csharp/10.ef-core/16.linq-queries-part2",{"title":563,"path":564,"stem":565},"Завантаження Пов'язаних Даних (Частина 1)","/csharp/ef-core/loading-related-data-part1","01.csharp/10.ef-core/17.loading-related-data-part1",{"title":567,"path":568,"stem":569},"Завантаження Пов'язаних Даних (Частина 2)","/csharp/ef-core/loading-related-data-part2","01.csharp/10.ef-core/17.loading-related-data-part2",{"title":571,"path":572,"stem":573},"Raw SQL, Views та Stored Procedures (Частина 1)","/csharp/ef-core/raw-sql-part1","01.csharp/10.ef-core/18.raw-sql-part1",{"title":575,"path":576,"stem":577},"Raw SQL — Stored Procedures, DbFunction та Bulk Operations (Частина 2)","/csharp/ef-core/raw-sql-part2","01.csharp/10.ef-core/18.raw-sql-part2",{"title":579,"path":580,"stem":581},"Продвинуті Запити — Compiled Queries, Bulk та Оптимізація (Частина 1)","/csharp/ef-core/advanced-queries-part1","01.csharp/10.ef-core/19.advanced-queries-part1",{"title":583,"path":584,"stem":585},"Продвинуті Запити — Query Tags, Bulk та Interceptors (Частина 2)","/csharp/ef-core/advanced-queries-part2","01.csharp/10.ef-core/19.advanced-queries-part2",{"title":587,"path":588,"stem":589},"Change Tracker — Відстеження Змін (Частина 1)","/csharp/ef-core/change-tracking-part1","01.csharp/10.ef-core/20.change-tracking-part1",{"title":591,"path":592,"stem":593},"Change Tracker — Графи Об'єктів та Disconnected (Частина 2)","/csharp/ef-core/change-tracking-part2","01.csharp/10.ef-core/20.change-tracking-part2",{"title":595,"path":596,"stem":597},"Збереження Даних та Транзакції (Частина 1)","/csharp/ef-core/saving-data-part1","01.csharp/10.ef-core/21.saving-data-part1",{"title":599,"path":600,"stem":601},"Збереження Даних — Concurrency та Outbox (Частина 2)","/csharp/ef-core/saving-data-part2","01.csharp/10.ef-core/21.saving-data-part2",{"title":603,"path":604,"stem":605},"Конкурентність та Блокування (Частина 1)","/csharp/ef-core/concurrency-part1","01.csharp/10.ef-core/22.concurrency-part1",{"title":607,"path":608,"stem":609},"Конкурентність — Дедлоки та Queue Processing (Частина 2)","/csharp/ef-core/concurrency-part2","01.csharp/10.ef-core/22.concurrency-part2",{"title":611,"path":612,"stem":613},"Міграції в EF Core — Основи (Частина 1)","/csharp/ef-core/migrations-basics-part1","01.csharp/10.ef-core/23.migrations-basics-part1",{"title":615,"path":616,"stem":617},"Міграції в EF Core — Основи (Частина 2)","/csharp/ef-core/migrations-basics-part2","01.csharp/10.ef-core/23.migrations-basics-part2",{"title":619,"path":620,"stem":621},"Міграції — Просунуті Сценарії (Частина 1)","/csharp/ef-core/migrations-advanced-part1","01.csharp/10.ef-core/24.migrations-advanced-part1",{"title":623,"path":624,"stem":625},"Міграції — Просунуті Сценарії (Частина 2)","/csharp/ef-core/migrations-advanced-part2","01.csharp/10.ef-core/24.migrations-advanced-part2",{"title":627,"path":628,"stem":629},"Управління Схемою та Database-First (Частина 1)","/csharp/ef-core/schema-management-part1","01.csharp/10.ef-core/25.schema-management-part1",{"title":631,"path":632,"stem":633},"Управління Схемою та Database-First (Частина 2)","/csharp/ef-core/schema-management-part2","01.csharp/10.ef-core/25.schema-management-part2",{"title":635,"path":636,"stem":637},"Продуктивність EF Core — Основи (Частина 1)","/csharp/ef-core/performance-fundamentals-part1","01.csharp/10.ef-core/26.performance-fundamentals-part1",{"title":639,"path":640,"stem":641},"Interceptors в EF Core (Частина 1)","/csharp/ef-core/interceptors-part1","01.csharp/10.ef-core/29.interceptors-part1",{"title":643,"path":644,"stem":645},"Interceptors в EF Core — Connection, Transaction та Materialization (Частина 2)","/csharp/ef-core/interceptors-part2","01.csharp/10.ef-core/29.interceptors-part2",{"title":647,"path":648,"stem":649},"План вивчення Entity Framework Core — Повний курс","/csharp/ef-core/implementation_plan","01.csharp/10.ef-core/implementation_plan",{"title":651,"icon":652,"path":653,"stem":654,"children":655,"page":59},"ASP.NET","i-devicon-dotnetcore","/csharp/aspnet","01.csharp/11.aspnet",[656,730,791,869,927,941,967,1057,1111,1182,1212,1289],{"title":657,"icon":658,"path":659,"stem":660,"children":661,"page":59},"Minimal API","i-lucide-network","/csharp/aspnet/minimal-api","01.csharp/11.aspnet/01.minimal-api",[662,666,670,674,678,682,686,690,694,698,702,706,710,714,718,722,726],{"title":663,"path":664,"stem":665},"Вступ до ASP.NET та еволюція фреймворку","/csharp/aspnet/minimal-api/introduction","01.csharp/11.aspnet/01.minimal-api/01.introduction",{"title":667,"path":668,"stem":669},"Перший додаток на ASP.NET Core","/csharp/aspnet/minimal-api/first-application","01.csharp/11.aspnet/01.minimal-api/02.first-application",{"title":671,"path":672,"stem":673},"WebApplication, Builder та Dependency Injection","/csharp/aspnet/minimal-api/webapplication-builder","01.csharp/11.aspnet/01.minimal-api/03.webapplication-builder",{"title":675,"path":676,"stem":677},"Конвеєр запитів та Middleware","/csharp/aspnet/minimal-api/request-pipeline-middleware","01.csharp/11.aspnet/01.minimal-api/04.request-pipeline-middleware",{"title":679,"path":680,"stem":681},"Маршрутизація в ASP.NET Core: Основи","/csharp/aspnet/minimal-api/routing-basics","01.csharp/11.aspnet/01.minimal-api/05.routing-basics",{"title":683,"path":684,"stem":685},"Маршрутизація в ASP.NET Core: Розширені можливості","/csharp/aspnet/minimal-api/routing-advanced","01.csharp/11.aspnet/01.minimal-api/06.routing-advanced",{"title":687,"path":688,"stem":689},"Статичні файли в ASP.NET Core","/csharp/aspnet/minimal-api/static-files","01.csharp/11.aspnet/01.minimal-api/07.static-files",{"title":691,"path":692,"stem":693},"Статичні Активи: MapStaticAssets (ASP.NET Core 9.0)","/csharp/aspnet/minimal-api/static-assets","01.csharp/11.aspnet/01.minimal-api/08.static-assets",{"title":695,"path":696,"stem":697},"Конфігурація в ASP.NET Core: Основи","/csharp/aspnet/minimal-api/configuration-fundamentals","01.csharp/11.aspnet/01.minimal-api/09.configuration-fundamentals",{"title":699,"path":700,"stem":701},"Конфігурація: Паттерн Options","/csharp/aspnet/minimal-api/configuration-options","01.csharp/11.aspnet/01.minimal-api/10.configuration-options",{"title":703,"path":704,"stem":705},"Логування в ASP.NET Core: Основи","/csharp/aspnet/minimal-api/logging-basics","01.csharp/11.aspnet/01.minimal-api/11.logging-basics",{"title":707,"path":708,"stem":709},"Логування: Serilog та Middleware","/csharp/aspnet/minimal-api/logging-advanced","01.csharp/11.aspnet/01.minimal-api/12.logging-advanced",{"title":711,"path":712,"stem":713},"Управління станом: HttpContext.Items та Cookies","/csharp/aspnet/minimal-api/state-management","01.csharp/11.aspnet/01.minimal-api/13.state-management",{"title":715,"path":716,"stem":717},"Стан сесії: Sessions","/csharp/aspnet/minimal-api/session-state","01.csharp/11.aspnet/01.minimal-api/14.session-state",{"title":719,"path":720,"stem":721},"Структура проєкту: від хаосу до архітектури","/csharp/aspnet/minimal-api/project-structure","01.csharp/11.aspnet/01.minimal-api/15.project-structure",{"title":723,"path":724,"stem":725},"Scalar у Minimal API: повний проєкт і Fluent OpenAPI","/csharp/aspnet/minimal-api/scalar-openapi-fluent","01.csharp/11.aspnet/01.minimal-api/16.scalar-openapi-fluent",{"title":727,"path":728,"stem":729},"Swagger / Swashbuckle у Minimal API: окремий класичний шлях","/csharp/aspnet/minimal-api/swagger-swashbuckle","01.csharp/11.aspnet/01.minimal-api/17.swagger-swashbuckle",{"title":731,"icon":658,"path":732,"stem":733,"children":734,"page":59},"API","/csharp/aspnet/api","01.csharp/11.aspnet/02.api",[735,739,743,747,751,755,759,763,767,771,775,779,783,787],{"title":736,"path":737,"stem":738},"Що таке API. Клієнт-серверна архітектура","/csharp/aspnet/api/what-is-api","01.csharp/11.aspnet/02.api/01.what-is-api",{"title":740,"path":741,"stem":742},"Формати даних: JSON, XML, TOML та бінарні формати","/csharp/aspnet/api/data-formats","01.csharp/11.aspnet/02.api/02.data-formats",{"title":744,"path":745,"stem":746},"Парадигми API та концепція REST","/csharp/aspnet/api/api-paradigms-rest","01.csharp/11.aspnet/02.api/03.api-paradigms-rest",{"title":748,"path":749,"stem":750},"HTTP-методи, статус-коди та заголовки","/csharp/aspnet/api/http-methods-status-codes","01.csharp/11.aspnet/02.api/04.http-methods-status-codes",{"title":752,"path":753,"stem":754},"Організація HTTP API за принципами REST","/csharp/aspnet/api/rest-organizing","01.csharp/11.aspnet/02.api/05.rest-organizing",{"title":756,"path":757,"stem":758},"Номенклатура URL та CRUD-операції","/csharp/aspnet/api/url-nomenclature-crud","01.csharp/11.aspnet/02.api/06.url-nomenclature-crud",{"title":760,"path":761,"stem":762},"Правила дизайну: іменування та стандарти","/csharp/aspnet/api/api-design-naming","01.csharp/11.aspnet/02.api/07.api-design-naming",{"title":764,"path":765,"stem":766},"Валідація, ліміти та обробка помилок","/csharp/aspnet/api/api-design-validation","01.csharp/11.aspnet/02.api/08.api-design-validation",{"title":768,"path":769,"stem":770},"Обробка помилок у Minimal API","/csharp/aspnet/api/error-handling-http","01.csharp/11.aspnet/02.api/09.error-handling-http",{"title":772,"path":773,"stem":774},"Ідемпотентність та синхронізація стану","/csharp/aspnet/api/idempotency-sync","01.csharp/11.aspnet/02.api/10.idempotency-sync",{"title":776,"path":777,"stem":778},"Пагінація та організація списків","/csharp/aspnet/api/pagination-lists","01.csharp/11.aspnet/02.api/11.pagination-lists",{"title":780,"path":781,"stem":782},"Безпека API, кешування та інтернаціоналізація","/csharp/aspnet/api/security-auth","01.csharp/11.aspnet/02.api/12.security-auth",{"title":784,"path":785,"stem":786},"Процес проєктування API та документування","/csharp/aspnet/api/api-design-process","01.csharp/11.aspnet/02.api/13.api-design-process",{"title":788,"path":789,"stem":790},"OpenAPI: контракт, специфікація та документація API","/csharp/aspnet/api/openapi","01.csharp/11.aspnet/02.api/14.openapi",{"title":792,"icon":793,"path":794,"stem":795,"children":796,"page":59},"Auth","i-lucide-shield-check","/csharp/aspnet/auth","01.csharp/11.aspnet/03.auth",[797,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865],{"title":798,"path":799,"stem":800},"Основи аутентифікації та авторизації","/csharp/aspnet/auth/auth-fundamentals","01.csharp/11.aspnet/03.auth/01.auth-fundamentals",{"title":802,"path":803,"stem":804},"JWT-аутентифікація","/csharp/aspnet/auth/jwt-authentication","01.csharp/11.aspnet/03.auth/02.jwt-authentication",{"title":806,"path":807,"stem":808},"Авторизація: ролі, політики та resource-based доступ","/csharp/aspnet/auth/authorization-policies","01.csharp/11.aspnet/03.auth/03.authorization-policies",{"title":810,"path":811,"stem":812},"Cookie-аутентифікація та ASP.NET Core Identity","/csharp/aspnet/auth/cookie-auth-identity","01.csharp/11.aspnet/03.auth/04.cookie-auth-identity",{"title":814,"path":815,"stem":816},"JWT + Refresh Tokens (HttpOnly Cookie)","/csharp/aspnet/auth/04b.identity-auth-jwt","01.csharp/11.aspnet/03.auth/04b.identity-auth-jwt",{"title":818,"path":819,"stem":820},"Identity: Підтвердження Email та Скидання Пароля","/csharp/aspnet/auth/identity-email-confirmation","01.csharp/11.aspnet/03.auth/05.identity-email-confirmation",{"title":822,"path":823,"stem":824},"Identity: Двофакторна Аутентифікація (2FA)","/csharp/aspnet/auth/identity-two-factor","01.csharp/11.aspnet/03.auth/06.identity-two-factor",{"title":826,"path":827,"stem":828},"Identity: Внутрішня Архітектура та Кастомізація","/csharp/aspnet/auth/identity-internals","01.csharp/11.aspnet/03.auth/07.identity-internals",{"title":830,"path":831,"stem":832},"OAuth 2.0 та зовнішні провайдери","/csharp/aspnet/auth/oauth-external-providers","01.csharp/11.aspnet/03.auth/08.oauth-external-providers",{"title":834,"path":835,"stem":836},"Безпека на практиці: CORS, HTTPS та захист від атак","/csharp/aspnet/auth/security-hardening","01.csharp/11.aspnet/03.auth/09.security-hardening",{"title":838,"path":839,"stem":840},"Теорія OAuth 2.0: Поняття, Аналогії та Флоу","/csharp/aspnet/auth/oauth-theory","01.csharp/11.aspnet/03.auth/10.oauth-theory",{"title":842,"path":843,"stem":844},"OIDC, OAuth 2.0 та Keycloak в ASP.NET Core","/csharp/aspnet/auth/oidc-keycloak","01.csharp/11.aspnet/03.auth/10.oidc-keycloak",{"title":846,"path":847,"stem":848},"API Keys аутентифікація в ASP.NET Core","/csharp/aspnet/auth/api-keys","01.csharp/11.aspnet/03.auth/11.api-keys",{"title":850,"path":851,"stem":852},"Rate Limiting та Throttling в ASP.NET Core","/csharp/aspnet/auth/rate-limiting","01.csharp/11.aspnet/03.auth/12.rate-limiting",{"title":854,"path":855,"stem":856},"Refresh Token Rotation в ASP.NET Core","/csharp/aspnet/auth/refresh-token-rotation","01.csharp/11.aspnet/03.auth/13.refresh-token-rotation",{"title":858,"path":859,"stem":860},"Certificate Authentication та mTLS в ASP.NET Core","/csharp/aspnet/auth/certificate-auth","01.csharp/11.aspnet/03.auth/14.certificate-auth",{"title":862,"path":863,"stem":864},"RBAC, ABAC та ReBAC в ASP.NET Core","/csharp/aspnet/auth/rbac-abac-rebac","01.csharp/11.aspnet/03.auth/15.rbac-abac-rebac",{"title":866,"path":867,"stem":868},"Multi-tenancy та ізоляція даних в ASP.NET Core","/csharp/aspnet/auth/multi-tenancy","01.csharp/11.aspnet/03.auth/16.multi-tenancy",{"title":870,"icon":871,"path":872,"stem":873,"children":874,"page":59},"Нотифікації","i-lucide-bell","/csharp/aspnet/notifications","01.csharp/11.aspnet/04.notifications",[875,879,883,887,891,895,899,903,907,911,915,919,923],{"title":876,"path":877,"stem":878},"In-App нотифікації через базу даних","/csharp/aspnet/notifications/in-app-database-notifications","01.csharp/11.aspnet/04.notifications/01.in-app-database-notifications",{"title":880,"path":881,"stem":882},"Polling: Регулярний запит оновлень","/csharp/aspnet/notifications/polling","01.csharp/11.aspnet/04.notifications/02.polling",{"title":884,"path":885,"stem":886},"Server-Sent Events: Однострімовий push від сервера","/csharp/aspnet/notifications/server-sent-events","01.csharp/11.aspnet/04.notifications/03.server-sent-events",{"title":888,"path":889,"stem":890},"WebSockets: Двостороннє з'єднання в реальному часі","/csharp/aspnet/notifications/websockets","01.csharp/11.aspnet/04.notifications/04.websockets",{"title":892,"path":893,"stem":894},"SignalR: Абстракція над транспортами реального часу","/csharp/aspnet/notifications/signalr","01.csharp/11.aspnet/04.notifications/05.signalr",{"title":896,"path":897,"stem":898},"Background Services: Фонові задачі в ASP.NET Core","/csharp/aspnet/notifications/background-services","01.csharp/11.aspnet/04.notifications/06.background-services",{"title":900,"path":901,"stem":902},"Web Push нотифікації","/csharp/aspnet/notifications/web-push","01.csharp/11.aspnet/04.notifications/07.web-push",{"title":904,"path":905,"stem":906},"Email нотифікації","/csharp/aspnet/notifications/email-notifications","01.csharp/11.aspnet/04.notifications/08.email-notifications",{"title":908,"path":909,"stem":910},"Порівняння підходів: Як вибрати правильну технологію нотифікацій","/csharp/aspnet/notifications/choosing-the-right-approach","01.csharp/11.aspnet/04.notifications/09.choosing-the-right-approach",{"title":912,"path":913,"stem":914},"Hangfire: Надійне планування фонових задач","/csharp/aspnet/notifications/hangfire","01.csharp/11.aspnet/04.notifications/10.hangfire",{"title":916,"path":917,"stem":918},"Практика: Конвертація зображень у WebP через Hangfire","/csharp/aspnet/notifications/hangfire-image-webp","01.csharp/11.aspnet/04.notifications/11.hangfire-image-webp",{"title":920,"path":921,"stem":922},"Практика: Підготовка відео до HLS-стрімінгу через Hangfire","/csharp/aspnet/notifications/hangfire-video-hls","01.csharp/11.aspnet/04.notifications/12.hangfire-video-hls",{"title":924,"path":925,"stem":926},"Telegram-нотифікації: від одного повідомлення до масових розсилок і мульти-канального підходу","/csharp/aspnet/notifications/telegram-notifications","01.csharp/11.aspnet/04.notifications/13.telegram-notifications",{"title":928,"icon":929,"path":930,"stem":931,"children":932,"page":59},"Інтернаціоналізація","i-lucide-languages","/csharp/aspnet/i18n","01.csharp/11.aspnet/05.i18n",[933,937],{"title":934,"path":935,"stem":936},"Інтернаціоналізація (i18n) у Minimal API: від A до Я","/csharp/aspnet/i18n/internationalization","01.csharp/11.aspnet/05.i18n/01.internationalization",{"title":938,"path":939,"stem":940},"Humanizer: людиномовні рядки у .NET","/csharp/aspnet/i18n/humanizer","01.csharp/11.aspnet/05.i18n/02.humanizer",{"title":942,"icon":943,"path":944,"stem":945,"children":946,"page":59},"Кешування","i-lucide-layers","/csharp/aspnet/caching","01.csharp/11.aspnet/06.caching",[947,951,955,959,963],{"title":948,"path":949,"stem":950},"Огляд кешування: чотири рівні і коли що обирати","/csharp/aspnet/caching/caching","01.csharp/11.aspnet/06.caching/01.caching",{"title":952,"path":953,"stem":954},"IMemoryCache: кеш в оперативній пам'яті","/csharp/aspnet/caching/memory-cache","01.csharp/11.aspnet/06.caching/02.memory-cache",{"title":956,"path":957,"stem":958},"IDistributedCache і Redis: розподілений кеш","/csharp/aspnet/caching/distributed-cache","01.csharp/11.aspnet/06.caching/03.distributed-cache",{"title":960,"path":961,"stem":962},"Response Cache: HTTP-кешування через Cache-Control","/csharp/aspnet/caching/response-cache","01.csharp/11.aspnet/06.caching/04.response-cache",{"title":964,"path":965,"stem":966},"Output Cache: серверний кеш HTTP-відповідей (.NET 7+)","/csharp/aspnet/caching/output-cache","01.csharp/11.aspnet/06.caching/05.output-cache",{"title":968,"icon":969,"path":970,"stem":971,"children":972,"page":59},"Тестування","i-lucide-test-tube","/csharp/aspnet/testing","01.csharp/11.aspnet/07.testing",[973,977,981,985,989,993,997,1001,1005,1009,1013,1017,1021,1025,1029,1033,1037,1041,1045,1049,1053],{"title":974,"path":975,"stem":976},"Що таке тестування? Від інтуїції до науки","/csharp/aspnet/testing/what-is-testing","01.csharp/11.aspnet/07.testing/01.what-is-testing",{"title":978,"path":979,"stem":980},"Піраміда тестування — Стратегія, а не Догма","/csharp/aspnet/testing/testing-pyramid","01.csharp/11.aspnet/07.testing/02.testing-pyramid",{"title":982,"path":983,"stem":984},"Дві Школи Тестування — Лондон проти Детройту","/csharp/aspnet/testing/testing-schools","01.csharp/11.aspnet/07.testing/03.testing-schools",{"title":986,"path":987,"stem":988},"TDD та BDD — Тести як Дизайн-інструмент","/csharp/aspnet/testing/tdd-and-bdd","01.csharp/11.aspnet/07.testing/04.tdd-and-bdd",{"title":990,"path":991,"stem":992},"Що саме тестувати — Техніки аналізу та Циклomatична складність","/csharp/aspnet/testing/what-to-test","01.csharp/11.aspnet/07.testing/05.what-to-test",{"title":994,"path":995,"stem":996},"Тестові Фреймворки — Навіщо вони і що всередині","/csharp/aspnet/testing/test-frameworks","01.csharp/11.aspnet/07.testing/06.test-frameworks",{"title":998,"path":999,"stem":1000},"xUnit — Факти, Теорії та Lifecycle тестів","/csharp/aspnet/testing/xunit-basics","01.csharp/11.aspnet/07.testing/07.xunit-basics",{"title":1002,"path":1003,"stem":1004},"xUnit Advanced — Fixtures, Кастомізація та Розширення","/csharp/aspnet/testing/xunit-advanced","01.csharp/11.aspnet/07.testing/08.xunit-advanced",{"title":1006,"path":1007,"stem":1008},"Moq — Глибоке занурення в мокування","/csharp/aspnet/testing/mocking-with-moq","01.csharp/11.aspnet/07.testing/09.mocking-with-moq",{"title":1010,"path":1011,"stem":1012},"Тестування Баз Даних — EF Core, SQLite та Testcontainers","/csharp/aspnet/testing/database-testing","01.csharp/11.aspnet/07.testing/10.database-testing",{"title":1014,"path":1015,"stem":1016},"Integration Testing — Частина 1 [Теорія та WebApplicationFactory]","/csharp/aspnet/testing/integration-testing","01.csharp/11.aspnet/07.testing/11.integration-testing",{"title":1018,"path":1019,"stem":1020},"Інтеграційне тестування — Практика","/csharp/aspnet/testing/11a.integration-testing-practice","01.csharp/11.aspnet/07.testing/11a.integration-testing-practice",{"title":1022,"path":1023,"stem":1024},"Integration Testing — Частина 2 [Просунуті Сценарії та Testcontainers]","/csharp/aspnet/testing/integration-testing-advanced","01.csharp/11.aspnet/07.testing/12.integration-testing-advanced",{"title":1026,"path":1027,"stem":1028},"Професійний Postman: Колекції, Змінні та GitHub Інтеграція","/csharp/aspnet/testing/postman-professional","01.csharp/11.aspnet/07.testing/13.postman-professional",{"title":1030,"path":1031,"stem":1032},"HttpClient у Тестах Частина 1: Архітектура та MockHttpMessageHandler","/csharp/aspnet/testing/httpclient-testing","01.csharp/11.aspnet/07.testing/14.httpclient-testing",{"title":1034,"path":1035,"stem":1036},"HttpClient у Тестах Частина 2: WireMock.Net та Resilience","/csharp/aspnet/testing/wiremock-net","01.csharp/11.aspnet/07.testing/15.wiremock-net",{"title":1038,"path":1039,"stem":1040},"Патерни та Анти-патерни Тестування: Test Smells","/csharp/aspnet/testing/testing-patterns","01.csharp/11.aspnet/07.testing/16.testing-patterns",{"title":1042,"path":1043,"stem":1044},"Просунуті інструменти: Time, Snapshots та Властивості","/csharp/aspnet/testing/advanced-testing-tools","01.csharp/11.aspnet/07.testing/17.advanced-testing-tools",{"title":1046,"path":1047,"stem":1048},"Тестування Архітектури з NetArchTest","/csharp/aspnet/testing/architecture-testing","01.csharp/11.aspnet/07.testing/18.architecture-testing",{"title":1050,"path":1051,"stem":1052},"Тестування Продуктивності: BenchmarkDotNet, NBomber та k6","/csharp/aspnet/testing/performance-testing","01.csharp/11.aspnet/07.testing/19.performance-testing",{"title":1054,"path":1055,"stem":1056},"Залишок плану для курсу \"Тестування ASP.NET Minimal API\"","/csharp/aspnet/testing/remaining_plan","01.csharp/11.aspnet/07.testing/remaining_plan",{"title":1058,"icon":1059,"path":1060,"stem":1061,"children":1062,"page":59},"Платежі","i-lucide-credit-card","/csharp/aspnet/payments","01.csharp/11.aspnet/08.payments",[1063,1067,1071,1075,1079,1083,1087,1091,1095,1099,1103,1107],{"title":1064,"path":1065,"stem":1066},"Основи платіжної інфраструктури","/csharp/aspnet/payments/payment-fundamentals","01.csharp/11.aspnet/08.payments/01.payment-fundamentals",{"title":1068,"path":1069,"stem":1070},"Методи оплати в Україні","/csharp/aspnet/payments/payment-methods-ukraine","01.csharp/11.aspnet/08.payments/02.payment-methods-ukraine",{"title":1072,"path":1073,"stem":1074},"PCI DSS та безпека платежів","/csharp/aspnet/payments/pci-dss-security","01.csharp/11.aspnet/08.payments/03.pci-dss-security",{"title":1076,"path":1077,"stem":1078},"Архітектура платіжної підсистеми","/csharp/aspnet/payments/payment-architecture","01.csharp/11.aspnet/08.payments/04.payment-architecture",{"title":1080,"path":1081,"stem":1082},"Інтеграція LiqPay (ПриватБанк)","/csharp/aspnet/payments/liqpay-integration","01.csharp/11.aspnet/08.payments/05.liqpay-integration",{"title":1084,"path":1085,"stem":1086},"Інтеграція Monobank Acquiring API","/csharp/aspnet/payments/monobank-acquiring","01.csharp/11.aspnet/08.payments/06.monobank-acquiring",{"title":1088,"path":1089,"stem":1090},"Інтеграція Stripe","/csharp/aspnet/payments/stripe-integration","01.csharp/11.aspnet/08.payments/07.stripe-integration",{"title":1092,"path":1093,"stem":1094},"Webhooks — глибоке занурення","/csharp/aspnet/payments/webhooks-deep-dive","01.csharp/11.aspnet/08.payments/08.webhooks-deep-dive",{"title":1096,"path":1097,"stem":1098},"Підписки та рекурентні платежі","/csharp/aspnet/payments/subscriptions-recurring","01.csharp/11.aspnet/08.payments/09.subscriptions-recurring",{"title":1100,"path":1101,"stem":1102},"Повернення коштів та диспути","/csharp/aspnet/payments/refunds-disputes","01.csharp/11.aspnet/08.payments/10.refunds-disputes",{"title":1104,"path":1105,"stem":1106},"Тестування платіжних інтеграцій","/csharp/aspnet/payments/testing-payments","01.csharp/11.aspnet/08.payments/11.testing-payments",{"title":1108,"path":1109,"stem":1110},"Чекліст виходу в Production","/csharp/aspnet/payments/production-checklist","01.csharp/11.aspnet/08.payments/12.production-checklist",{"title":1112,"icon":1113,"items":1114,"path":1127,"stem":1128,"children":1129,"page":59},"Популярні бібліотеки","lucide:box",[1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126],"01.fluent-validation","02.mapster","03.erroror-result-pattern","04.serilog","05.mediatr","06.polly","07.health-checks","08.feature-management","09.fluent-email","10.quest-pdf","11.bogus","12.humanizer-guard","/csharp/aspnet/libraries","01.csharp/11.aspnet/09.libraries",[1130,1134,1138,1142,1146,1150,1154,1158,1162,1166,1170,1174,1178],{"title":1131,"path":1132,"stem":1133},"Валідація з FluentValidation в ASP.NET Core","/csharp/aspnet/libraries/fluent-validation","01.csharp/11.aspnet/09.libraries/01.fluent-validation",{"title":1135,"path":1136,"stem":1137},"Маппінг об","/csharp/aspnet/libraries/mapster","01.csharp/11.aspnet/09.libraries/02.mapster",{"title":1139,"path":1140,"stem":1141},"Обробка помилок з ErrorOr та Result Pattern в ASP.NET Core","/csharp/aspnet/libraries/erroror-result-pattern","01.csharp/11.aspnet/09.libraries/03.erroror-result-pattern",{"title":1143,"path":1144,"stem":1145},"Структуроване логування з Serilog в ASP.NET Core","/csharp/aspnet/libraries/serilog","01.csharp/11.aspnet/09.libraries/04.serilog",{"title":1147,"path":1148,"stem":1149},"CQRS та Mediator з MediatR в ASP.NET Core","/csharp/aspnet/libraries/mediatr","01.csharp/11.aspnet/09.libraries/05.mediatr",{"title":1151,"path":1152,"stem":1153},"Відмовостійкість з Polly в ASP.NET Core","/csharp/aspnet/libraries/polly","01.csharp/11.aspnet/09.libraries/06.polly",{"title":1155,"path":1156,"stem":1157},"Health Checks в ASP.NET Core","/csharp/aspnet/libraries/health-checks","01.csharp/11.aspnet/09.libraries/07.health-checks",{"title":1159,"path":1160,"stem":1161},"Feature Management та Feature Flags в ASP.NET Core","/csharp/aspnet/libraries/feature-management","01.csharp/11.aspnet/09.libraries/08.feature-management",{"title":1163,"path":1164,"stem":1165},"Відправка Email з FluentEmail в ASP.NET Core","/csharp/aspnet/libraries/fluent-email","01.csharp/11.aspnet/09.libraries/09.fluent-email",{"title":1167,"path":1168,"stem":1169},"Генерація PDF з QuestPDF в ASP.NET Core","/csharp/aspnet/libraries/quest-pdf","01.csharp/11.aspnet/09.libraries/10.quest-pdf",{"title":1171,"path":1172,"stem":1173},"Генерація тестових даних з Bogus в ASP.NET Core","/csharp/aspnet/libraries/bogus","01.csharp/11.aspnet/09.libraries/11.bogus",{"title":1175,"path":1176,"stem":1177},"Humanizer та Guard Clauses в ASP.NET Core","/csharp/aspnet/libraries/humanizer-guard","01.csharp/11.aspnet/09.libraries/12.humanizer-guard",{"title":1179,"path":1180,"stem":1181},"План модуля 10.libraries — Популярні бібліотеки ASP.NET","/csharp/aspnet/libraries/plan","01.csharp/11.aspnet/09.libraries/plan",{"title":1183,"icon":1184,"path":1185,"stem":1186,"children":1187,"page":59},"Razor Pages","i-lucide-layout-template","/csharp/aspnet/razor-pages","01.csharp/11.aspnet/10.razor-pages",[1188,1192,1196,1200,1204,1208],{"title":1189,"path":1190,"stem":1191},"Від Minimal API до Razor Pages: концептуальний перехід","/csharp/aspnet/razor-pages/from-minimal-api","01.csharp/11.aspnet/10.razor-pages/01.from-minimal-api",{"title":1193,"path":1194,"stem":1195},"PageModel: логіка сторінки Razor Pages","/csharp/aspnet/razor-pages/page-model","01.csharp/11.aspnet/10.razor-pages/02.page-model",{"title":1197,"path":1198,"stem":1199},"Razor синтаксис: шаблонізатор у .cshtml","/csharp/aspnet/razor-pages/razor-syntax","01.csharp/11.aspnet/10.razor-pages/03.razor-syntax",{"title":1201,"path":1202,"stem":1203},"Tag Helpers: типізований HTML","/csharp/aspnet/razor-pages/tag-helpers","01.csharp/11.aspnet/10.razor-pages/04.tag-helpers",{"title":1205,"path":1206,"stem":1207},"Форми і валідація: повний цикл обробки даних","/csharp/aspnet/razor-pages/forms-validation","01.csharp/11.aspnet/10.razor-pages/05.forms-validation",{"title":1209,"path":1210,"stem":1211},"Практичний проєкт: TaskManager на Razor Pages","/csharp/aspnet/razor-pages/project-task-manager","01.csharp/11.aspnet/10.razor-pages/06.project-task-manager",{"title":1213,"path":1214,"stem":1215,"children":1216,"page":59},"ASP.NET Core MVC","/csharp/aspnet/mvc","01.csharp/11.aspnet/11.mvc",[1217,1221,1225,1229,1233,1237,1241,1245,1249,1253,1257,1261,1265,1269,1273,1277,1281,1285],{"title":1218,"path":1219,"stem":1220},"Патерн MVC: архітектура, що змінила веб","/csharp/aspnet/mvc/mvc-pattern","01.csharp/11.aspnet/11.mvc/01.mvc-pattern",{"title":1222,"path":1223,"stem":1224},"Від Razor Pages до MVC: концептуальний перехід","/csharp/aspnet/mvc/from-razor-pages","01.csharp/11.aspnet/11.mvc/02.from-razor-pages",{"title":1226,"path":1227,"stem":1228},"Controllers та Actions: серце MVC","/csharp/aspnet/mvc/controllers-actions","01.csharp/11.aspnet/11.mvc/03.controllers-actions",{"title":1230,"path":1231,"stem":1232},"Маршрутизація в MVC: Convention vs Attribute Routing","/csharp/aspnet/mvc/routing-mvc","01.csharp/11.aspnet/11.mvc/04.routing-mvc",{"title":1234,"path":1235,"stem":1236},"Model Binding: від HTTP до C#","/csharp/aspnet/mvc/model-binding","01.csharp/11.aspnet/11.mvc/05.model-binding",{"title":1238,"path":1239,"stem":1240},"Views, ViewData, ViewBag, TempData і ViewModel","/csharp/aspnet/mvc/views-viewdata-tempdata","01.csharp/11.aspnet/11.mvc/06.views-viewdata-tempdata",{"title":1242,"path":1243,"stem":1244},"Filters: аспектно-орієнтоване програмування в MVC","/csharp/aspnet/mvc/filters","01.csharp/11.aspnet/11.mvc/07.filters",{"title":1246,"path":1247,"stem":1248},"Areas: структурування великих застосунків","/csharp/aspnet/mvc/areas","01.csharp/11.aspnet/11.mvc/08.areas",{"title":1250,"path":1251,"stem":1252},"View Components: повторювані незалежні блоки UI","/csharp/aspnet/mvc/view-components","01.csharp/11.aspnet/11.mvc/09.view-components",{"title":1254,"path":1255,"stem":1256},"Display та Editor Templates","/csharp/aspnet/mvc/display-editor-templates","01.csharp/11.aspnet/11.mvc/10.display-editor-templates",{"title":1258,"path":1259,"stem":1260},"Валідація: IValidatableObject та FluentValidation","/csharp/aspnet/mvc/validation-advanced","01.csharp/11.aspnet/11.mvc/11.validation-advanced",{"title":1262,"path":1263,"stem":1264},"HTMX: інтерактивність через HTML-атрибути","/csharp/aspnet/mvc/htmx","01.csharp/11.aspnet/11.mvc/12.htmx",{"title":1266,"path":1267,"stem":1268},"HTMX у ASP.NET Core MVC: серверна інтеграція","/csharp/aspnet/mvc/ajax-htmx-mvc","01.csharp/11.aspnet/11.mvc/13.ajax-htmx-mvc",{"title":1270,"path":1271,"stem":1272},"Практичний проєкт: Каталог товарів з HTMX","/csharp/aspnet/mvc/htmx-project","01.csharp/11.aspnet/11.mvc/14.htmx-project",{"title":1274,"path":1275,"stem":1276},"Завантаження та обробка файлів","/csharp/aspnet/mvc/file-upload","01.csharp/11.aspnet/11.mvc/15.file-upload",{"title":1278,"path":1279,"stem":1280},"Глобалізація та Локалізація MVC","/csharp/aspnet/mvc/globalization-localization","01.csharp/11.aspnet/11.mvc/16.globalization-localization",{"title":1282,"path":1283,"stem":1284},"Підсумковий проєкт: Блог-платформа","/csharp/aspnet/mvc/mvc-project","01.csharp/11.aspnet/11.mvc/17.mvc-project",{"title":1286,"path":1287,"stem":1288},"План курсу: ASP.NET Core MVC","/csharp/aspnet/mvc/plan","01.csharp/11.aspnet/11.mvc/plan",{"title":1290,"path":1291,"stem":1292,"children":1293,"page":59},"Web Api","/csharp/aspnet/web-api","01.csharp/11.aspnet/12.web-api",[1294,1298,1302,1306,1310,1314,1318,1322,1326,1330,1334,1338,1342],{"title":1295,"path":1296,"stem":1297},"Від Minimal API до Controller-based API","/csharp/aspnet/web-api/from-minimal-api-to-controllers","01.csharp/11.aspnet/12.web-api/01.from-minimal-api-to-controllers",{"title":1299,"path":1300,"stem":1301},"ControllerBase, ActionResult\u003CT> та Response Types","/csharp/aspnet/web-api/controller-base-actionresult","01.csharp/11.aspnet/12.web-api/02.controller-base-actionresult",{"title":1303,"path":1304,"stem":1305},"Content Negotiation - JSON, XML та власні форматери","/csharp/aspnet/web-api/content-negotiation","01.csharp/11.aspnet/12.web-api/03.content-negotiation",{"title":1307,"path":1308,"stem":1309},"Версіонування API","/csharp/aspnet/web-api/api-versioning","01.csharp/11.aspnet/12.web-api/04.api-versioning",{"title":1311,"path":1312,"stem":1313},"ProblemDetails та структурована обробка помилок","/csharp/aspnet/web-api/problemdetails-error-handling","01.csharp/11.aspnet/12.web-api/05.problemdetails-error-handling",{"title":1315,"path":1316,"stem":1317},"Фільтри у Web API контексті","/csharp/aspnet/web-api/filters-for-api","01.csharp/11.aspnet/12.web-api/06.filters-for-api",{"title":1319,"path":1320,"stem":1321},"Пагінація, фільтрація та сортування","/csharp/aspnet/web-api/pagination-filtering-sorting","01.csharp/11.aspnet/12.web-api/07.pagination-filtering-sorting",{"title":1323,"path":1324,"stem":1325},"HATEOAS та Resource Expansion","/csharp/aspnet/web-api/hateoas-resource-expansion","01.csharp/11.aspnet/12.web-api/08.hateoas-resource-expansion",{"title":1327,"path":1328,"stem":1329},"Гібридна архітектура - Minimal API + Controllers","/csharp/aspnet/web-api/minimal-api-vs-controllers-hybrid","01.csharp/11.aspnet/12.web-api/09.minimal-api-vs-controllers-hybrid",{"title":1331,"path":1332,"stem":1333},"Документація API - Swashbuckle, NSwag та генерація клієнтів","/csharp/aspnet/web-api/api-documentation-generation","01.csharp/11.aspnet/12.web-api/10.api-documentation-generation",{"title":1335,"path":1336,"stem":1337},"Health Checks та моніторинг API","/csharp/aspnet/web-api/health-checks-monitoring","01.csharp/11.aspnet/12.web-api/11.health-checks-monitoring",{"title":1339,"path":1340,"stem":1341},"Підсумковий проєкт - Production-Ready REST API","/csharp/aspnet/web-api/web-api-project","01.csharp/11.aspnet/12.web-api/12.web-api-project",{"title":1343,"path":1344,"stem":1345},"План курсу: ASP.NET Core Web API (Controllers)","/csharp/aspnet/web-api/plan","01.csharp/11.aspnet/12.web-api/plan",{"title":1347,"icon":1348,"path":1349,"stem":1350,"children":1351,"page":59},"Desktop UI","i-lucide-app-window","/csharp/desktop-ui","01.csharp/12.desktop-ui",[1352,1356,1360,1364,1368,1372,1376,1380,1384,1388,1392,1396,1400,1404,1408,1412,1416,1420,1424,1428,1432,1436,1440,1444,1448,1452,1456,1460,1464,1468,1472,1476,1480,1484,1488,1492,1496,1500,1504,1508,1512,1516,1520,1524,1528,1532,1536,1540,1544,1548,1552,1556,1560,1564,1568,1572,1576,1580,1584,1588,1592,1596,1600,1604,1608,1612,1616,1620,1624,1628,1632],{"title":1353,"path":1354,"stem":1355},"Що таке десктопна розробка?","/csharp/desktop-ui/what-is-desktop-dev","01.csharp/12.desktop-ui/01.what-is-desktop-dev",{"title":1357,"path":1358,"stem":1359},"Архітектура WPF — як влаштований графічний інтерфейс","/csharp/desktop-ui/wpf-architecture","01.csharp/12.desktop-ui/02.wpf-architecture",{"title":1361,"path":1362,"stem":1363},"Перший WPF-проєкт — від нуля до вікна","/csharp/desktop-ui/first-wpf-app","01.csharp/12.desktop-ui/03.first-wpf-app",{"title":1365,"path":1366,"stem":1367},"Перший Avalonia-проєкт: WPF для всіх платформ","/csharp/desktop-ui/03a.first-avalonia-app","01.csharp/12.desktop-ui/03a.first-avalonia-app",{"title":1369,"path":1370,"stem":1371},"XAML: декларативний інтерфейс","/csharp/desktop-ui/xaml-basics","01.csharp/12.desktop-ui/04.xaml-basics",{"title":1373,"path":1374,"stem":1375},"Fluent UI у WPF — сучасний дизайн Windows 11","/csharp/desktop-ui/04a.wpf-fluent-ui","01.csharp/12.desktop-ui/04a.wpf-fluent-ui",{"title":1377,"path":1378,"stem":1379},"WPF UI — сучасна бібліотека Fluent контролів","/csharp/desktop-ui/04b.wpf-ui-library","01.csharp/12.desktop-ui/04b.wpf-ui-library",{"title":1381,"path":1382,"stem":1383},"HandyControl — велика бібліотека UI контролів для WPF","/csharp/desktop-ui/04c.handycontrol-library","01.csharp/12.desktop-ui/04c.handycontrol-library",{"title":1385,"path":1386,"stem":1387},"Простори імен та ресурси XAML","/csharp/desktop-ui/xaml-namespaces-resources","01.csharp/12.desktop-ui/05.xaml-namespaces-resources",{"title":1389,"path":1390,"stem":1391},"XAML в Avalonia: ключові відмінності від WPF","/csharp/desktop-ui/05a.avalonia-xaml-differences","01.csharp/12.desktop-ui/05a.avalonia-xaml-differences",{"title":1393,"path":1394,"stem":1395},"Розширення розмітки XAML (Markup Extensions)","/csharp/desktop-ui/xaml-markup-extensions","01.csharp/12.desktop-ui/06.xaml-markup-extensions",{"title":1397,"path":1398,"stem":1399},"Панелі Layout: StackPanel, WrapPanel, DockPanel","/csharp/desktop-ui/layout-panels-part1","01.csharp/12.desktop-ui/07.layout-panels-part1",{"title":1401,"path":1402,"stem":1403},"Grid, Canvas, UniformGrid","/csharp/desktop-ui/layout-panels-part2","01.csharp/12.desktop-ui/07.layout-panels-part2",{"title":1405,"path":1406,"stem":1407},"Просунуті техніки Layout","/csharp/desktop-ui/layout-advanced","01.csharp/12.desktop-ui/08.layout-advanced",{"title":1409,"path":1410,"stem":1411},"Адаптивний Layout та найкращі практики","/csharp/desktop-ui/layout-responsive","01.csharp/12.desktop-ui/09.layout-responsive",{"title":1413,"path":1414,"stem":1415},"Layout в Avalonia: відмінності та нові можливості","/csharp/desktop-ui/09a.layout-avalonia","01.csharp/12.desktop-ui/09a.layout-avalonia",{"title":1417,"path":1418,"stem":1419},"Button, Image, ProgressBar та інші базові контроли","/csharp/desktop-ui/basic-controls","01.csharp/12.desktop-ui/10.basic-controls",{"title":1421,"path":1422,"stem":1423},"Контроли в Avalonia: відмінності від WPF","/csharp/desktop-ui/10a.controls-avalonia","01.csharp/12.desktop-ui/10a.controls-avalonia",{"title":1425,"path":1426,"stem":1427},"Текстові контроли — TextBlock, TextBox, RichTextBox","/csharp/desktop-ui/text-controls","01.csharp/12.desktop-ui/11.text-controls",{"title":1429,"path":1430,"stem":1431},"Контроли вибору — CheckBox, RadioButton, ComboBox, ListBox, DatePicker","/csharp/desktop-ui/selection-controls","01.csharp/12.desktop-ui/12.selection-controls",{"title":1433,"path":1434,"stem":1435},"Content Model — GroupBox, Expander, TabControl, StatusBar","/csharp/desktop-ui/content-controls","01.csharp/12.desktop-ui/13.content-controls",{"title":1437,"path":1438,"stem":1439},"UI/UX принципи десктопних застосунків","/csharp/desktop-ui/13a.ui-ux-principles","01.csharp/12.desktop-ui/13a.ui-ux-principles",{"title":1441,"path":1442,"stem":1443},"Dependency Properties — Концепція та Value Resolution","/csharp/desktop-ui/dependency-properties-part1","01.csharp/12.desktop-ui/14.dependency-properties-part1",{"title":1445,"path":1446,"stem":1447},"Avalonia Property System — StyledProperty та DirectProperty","/csharp/desktop-ui/14a.avalonia-property-system","01.csharp/12.desktop-ui/14a.avalonia-property-system",{"title":1449,"path":1450,"stem":1451},"Attached Properties — Властивості без меж","/csharp/desktop-ui/attached-properties","01.csharp/12.desktop-ui/15.attached-properties",{"title":1453,"path":1454,"stem":1455},"Routed Events — Маршрутизація подій у WPF","/csharp/desktop-ui/routed-events","01.csharp/12.desktop-ui/16.routed-events",{"title":1457,"path":1458,"stem":1459},"Data Binding — Від Code-Behind до Декларативності","/csharp/desktop-ui/data-binding-basics-part1","01.csharp/12.desktop-ui/17.data-binding-basics-part1",{"title":1461,"path":1462,"stem":1463},"INotifyPropertyChanged — Живе оновлення UI","/csharp/desktop-ui/data-binding-basics-part2","01.csharp/12.desktop-ui/17.data-binding-basics-part2",{"title":1465,"path":1466,"stem":1467},"Compiled Bindings в Avalonia — Безпека на етапі компіляції","/csharp/desktop-ui/17a.avalonia-compiled-bindings","01.csharp/12.desktop-ui/17a.avalonia-compiled-bindings",{"title":1469,"path":1470,"stem":1471},"Просунутий Data Binding — ElementName, RelativeSource, MultiBinding","/csharp/desktop-ui/data-binding-advanced","01.csharp/12.desktop-ui/18.data-binding-advanced",{"title":1473,"path":1474,"stem":1475},"Value Converters — Перетворення типів даних у Data Binding","/csharp/desktop-ui/value-converters","01.csharp/12.desktop-ui/19.value-converters",{"title":1477,"path":1478,"stem":1479},"Data Templates — Візуалізація об'єктів у WPF","/csharp/desktop-ui/data-templates","01.csharp/12.desktop-ui/20.data-templates",{"title":1481,"path":1482,"stem":1483},"Collections Binding Part 1 — ObservableCollection та ItemsControl","/csharp/desktop-ui/collections-binding-part1","01.csharp/12.desktop-ui/21.collections-binding-part1",{"title":1485,"path":1486,"stem":1487},"Collections Binding Part 2 — ICollectionView, Filtering, Sorting та Virtualization","/csharp/desktop-ui/collections-binding-part2","01.csharp/12.desktop-ui/21.collections-binding-part2",{"title":1489,"path":1490,"stem":1491},"MVVM Pattern — Від Spaghetti Code до архітектури","/csharp/desktop-ui/mvvm-pattern","01.csharp/12.desktop-ui/22.mvvm-pattern",{"title":1493,"path":1494,"stem":1495},"ViewModel Implementation — Від BaseViewModel до валідації","/csharp/desktop-ui/viewmodel-implementation","01.csharp/12.desktop-ui/23.viewmodel-implementation",{"title":1497,"path":1498,"stem":1499},"Commands — Від event handlers до декларативних команд","/csharp/desktop-ui/commands","01.csharp/12.desktop-ui/24.commands",{"title":1501,"path":1502,"stem":1503},"MVVM Toolkit — MVVM без boilerplate через Source Generators","/csharp/desktop-ui/mvvm-toolkit","01.csharp/12.desktop-ui/25.mvvm-toolkit",{"title":1505,"path":1506,"stem":1507},"Messenger Pattern — Комунікація між ViewModel без прямих посилань","/csharp/desktop-ui/messenger-pattern","01.csharp/12.desktop-ui/26.messenger-pattern",{"title":1509,"path":1510,"stem":1511},"Стилі WPF — CSS для десктопу","/csharp/desktop-ui/styles-basics","01.csharp/12.desktop-ui/27.styles-basics",{"title":1513,"path":1514,"stem":1515},"CSS-like стилі Avalonia","/csharp/desktop-ui/27a.avalonia-css-styling","01.csharp/12.desktop-ui/27a.avalonia-css-styling",{"title":1517,"path":1518,"stem":1519},"Control Templates — Частина 1. Концепція та TemplateBinding","/csharp/desktop-ui/control-templates-part1","01.csharp/12.desktop-ui/28.control-templates-part1",{"title":1521,"path":1522,"stem":1523},"Control Templates — Частина 2. Named Parts та ContentPresenter","/csharp/desktop-ui/control-templates-part2","01.csharp/12.desktop-ui/28.control-templates-part2",{"title":1525,"path":1526,"stem":1527},"Control Themes в Avalonia — нова ера стилізації","/csharp/desktop-ui/28a.avalonia-control-themes","01.csharp/12.desktop-ui/28a.avalonia-control-themes",{"title":1529,"path":1530,"stem":1531},"Triggers та Visual State Manager у WPF","/csharp/desktop-ui/triggers-visual-states","01.csharp/12.desktop-ui/29.triggers-visual-states",{"title":1533,"path":1534,"stem":1535},"Pseudo-classes в Avalonia — замість WPF Triggers","/csharp/desktop-ui/29a.avalonia-pseudo-classes","01.csharp/12.desktop-ui/29a.avalonia-pseudo-classes",{"title":1537,"path":1538,"stem":1539},"Теми та ресурсні словники у WPF","/csharp/desktop-ui/resources-themes","01.csharp/12.desktop-ui/30.resources-themes",{"title":1541,"path":1542,"stem":1543},"Avalonia Themes — Fluent Design та система тематизації","/csharp/desktop-ui/30a.avalonia-themes-fluent","01.csharp/12.desktop-ui/30a.avalonia-themes-fluent",{"title":1545,"path":1546,"stem":1547},"Контроли колекцій — глибоке занурення","/csharp/desktop-ui/collection-controls","01.csharp/12.desktop-ui/31.collection-controls",{"title":1549,"path":1550,"stem":1551},"DataGrid — колонки та базове відображення","/csharp/desktop-ui/datagrid-part1","01.csharp/12.desktop-ui/32.datagrid-part1",{"title":1553,"path":1554,"stem":1555},"DataGrid — сортування, фільтрація, редагування","/csharp/desktop-ui/datagrid-part2","01.csharp/12.desktop-ui/32.datagrid-part2",{"title":1557,"path":1558,"stem":1559},"TreeView та GridView","/csharp/desktop-ui/treeview-listview","01.csharp/12.desktop-ui/33.treeview-listview",{"title":1561,"path":1562,"stem":1563},"Меню, Toolbar, ContextMenu, StatusBar","/csharp/desktop-ui/menus-toolbars","01.csharp/12.desktop-ui/34.menus-toolbars",{"title":1565,"path":1566,"stem":1567},"Навігація та керування вікнами. Частина 1: вікна та сторінки","/csharp/desktop-ui/navigation-windows-part1","01.csharp/12.desktop-ui/35.navigation-windows-part1",{"title":1569,"path":1570,"stem":1571},"Навігація та керування вікнами. Частина 2: MVVM-навігація","/csharp/desktop-ui/navigation-windows-part2","01.csharp/12.desktop-ui/35.navigation-windows-part2",{"title":1573,"path":1574,"stem":1575},"Avalonia — Навігація та діалоги","/csharp/desktop-ui/35a.avalonia-navigation-dialogs","01.csharp/12.desktop-ui/35a.avalonia-navigation-dialogs",{"title":1577,"path":1578,"stem":1579},"Діалоги та File Pickers у WPF","/csharp/desktop-ui/dialogs-file-pickers","01.csharp/12.desktop-ui/36.dialogs-file-pickers",{"title":1581,"path":1582,"stem":1583},"UserControl: компонентний підхід у WPF","/csharp/desktop-ui/user-controls","01.csharp/12.desktop-ui/37.user-controls",{"title":1585,"path":1586,"stem":1587},"Custom Controls: Lookless Controls у WPF","/csharp/desktop-ui/custom-controls","01.csharp/12.desktop-ui/38.custom-controls",{"title":1589,"path":1590,"stem":1591},"Avalonia TemplatedControl — Lookless Controls","/csharp/desktop-ui/38a.avalonia-templated-controls","01.csharp/12.desktop-ui/38a.avalonia-templated-controls",{"title":1593,"path":1594,"stem":1595},"Анімації у WPF: Storyboard та Easing Functions","/csharp/desktop-ui/animations-transitions","01.csharp/12.desktop-ui/39.animations-transitions",{"title":1597,"path":1598,"stem":1599},"Анімації в Avalonia","/csharp/desktop-ui/39a.avalonia-animations","01.csharp/12.desktop-ui/39a.avalonia-animations",{"title":1601,"path":1602,"stem":1603},"2D Графіка та Мультимедіа у WPF","/csharp/desktop-ui/media-graphics","01.csharp/12.desktop-ui/40.media-graphics",{"title":1605,"path":1606,"stem":1607},"Dependency Injection у WPF та Avalonia","/csharp/desktop-ui/di-integration","01.csharp/12.desktop-ui/41.di-integration",{"title":1609,"path":1610,"stem":1611},"SQLite та EF Core у десктопних додатках","/csharp/desktop-ui/data-persistence-part1","01.csharp/12.desktop-ui/42.data-persistence-part1",{"title":1613,"path":1614,"stem":1615},"Repository Pattern та Unit of Work","/csharp/desktop-ui/data-persistence-part2","01.csharp/12.desktop-ui/43.data-persistence-part2",{"title":1617,"path":1618,"stem":1619},"Тестування ViewModels","/csharp/desktop-ui/viewmodel-testing","01.csharp/12.desktop-ui/44.viewmodel-testing",{"title":1621,"path":1622,"stem":1623},"Avalonia Headless Testing — тестування UI без вікон","/csharp/desktop-ui/44a.avalonia-headless-testing","01.csharp/12.desktop-ui/44a.avalonia-headless-testing",{"title":1625,"path":1626,"stem":1627},"Кросплатформна розробка з Avalonia","/csharp/desktop-ui/avalonia-cross-platform","01.csharp/12.desktop-ui/45.avalonia-cross-platform",{"title":1629,"path":1630,"stem":1631},"Пакування та розгортання Avalonia додатків","/csharp/desktop-ui/avalonia-packaging-deployment","01.csharp/12.desktop-ui/46.avalonia-packaging-deployment",{"title":1633,"path":1634,"stem":1635},"Розгортання WPF застосунків","/csharp/desktop-ui/wpf-packaging-deployment","01.csharp/12.desktop-ui/47.wpf-packaging-deployment",{"title":1637,"icon":658,"path":1638,"stem":1639,"children":1640,"page":59},"Network Programming","/csharp/network-programming","01.csharp/13.network-programming",[1641,1645,1649],{"title":1642,"path":1643,"stem":1644},"Основи комп'ютерних мереж","/csharp/network-programming/foundations","01.csharp/13.network-programming/01.foundations",{"title":1646,"path":1647,"stem":1648},"Модель OSI та стек TCP/IP","/csharp/network-programming/osi-model","01.csharp/13.network-programming/02.osi-model",{"title":1650,"path":1651,"stem":1652},"IP-протокол та адресація","/csharp/network-programming/ip-addressing","01.csharp/13.network-programming/03.ip-addressing",{"title":1654,"path":1655,"stem":1656},"C# & .NET: The Ultimate Roadmap","/csharp/roadmap","01.csharp/roadmap",{"title":1658,"icon":1659,"path":1660,"stem":1661,"children":1662,"page":59},"C++","i-devicon-cplusplus","/cpp","02.cpp",[1663,1667,1671,1675,1679,1683,1687,1691,1695,1698,1702,1706,1710,1714,1718,1722,1726,1730,1734,1738,1742,1746,1750,1754,1758,1762,1766,1770,1774,1778,1782,1786,1790,1794,1798,1802,1806],{"title":1664,"path":1665,"stem":1666},"Вступ у програмування та алгоритми","/cpp/intro-algorithms","02.cpp/01.intro-algorithms",{"title":1668,"path":1669,"stem":1670},"Code Style: угоди про оформлення коду","/cpp/code-style","02.cpp/02.code-style",{"title":1672,"path":1673,"stem":1674},"Середовище розробки та перший проєкт","/cpp/ide-setup","02.cpp/03.ide-setup",{"title":1676,"path":1677,"stem":1678},"Вивід даних на екран","/cpp/data-output","02.cpp/04.data-output",{"title":1680,"path":1681,"stem":1682},"Типи даних, змінні та константи","/cpp/data-types-variables","02.cpp/05.data-types-variables",{"title":1684,"path":1685,"stem":1686},"Ввід даних з клавіатури","/cpp/data-input","02.cpp/06.data-input",{"title":1688,"path":1689,"stem":1690},"Оператори, перетворення типів та логічні операції","/cpp/operators-type-conversion","02.cpp/07.operators-type-conversion",{"title":1692,"path":1693,"stem":1694},"Цикли","/cpp/loops","02.cpp/08.loops",{"title":32,"path":1696,"stem":1697},"/cpp/arrays","02.cpp/09.arrays",{"title":1699,"path":1700,"stem":1701},"Алгоритми сортування та аналіз складності","/cpp/sorting","02.cpp/10.sorting",{"title":1703,"path":1704,"stem":1705},"Алгоритми пошуку","/cpp/searching","02.cpp/11.searching",{"title":1707,"path":1708,"stem":1709},"Функції: основи","/cpp/functions-basics","02.cpp/12.functions-basics",{"title":1711,"path":1712,"stem":1713},"Функції: прототипи, область видимості та додаткові можливості","/cpp/functions-scope","02.cpp/13.functions-scope",{"title":1715,"path":1716,"stem":1717},"Функції: перевантаження та шаблони","/cpp/functions-overloading-templates","02.cpp/14.functions-overloading-templates",{"title":1719,"path":1720,"stem":1721},"Вказівники: основи","/cpp/pointers-basics","02.cpp/15.pointers-basics",{"title":1723,"path":1724,"stem":1725},"Посилання (References)","/cpp/references","02.cpp/16.references",{"title":1727,"path":1728,"stem":1729},"Вказівники, const і масиви","/cpp/pointers-const-arrays","02.cpp/17.pointers-const-arrays",{"title":1731,"path":1732,"stem":1733},"Адресна арифметика","/cpp/pointer-arithmetic","02.cpp/18.pointer-arithmetic",{"title":1735,"path":1736,"stem":1737},"Динамічна пам'ять","/cpp/dynamic-memory","02.cpp/19.dynamic-memory",{"title":1739,"path":1740,"stem":1741},"Вказівники типу void","/cpp/void-pointers","02.cpp/20.void-pointers",{"title":1743,"path":1744,"stem":1745},"Вказівники на вказівники","/cpp/pointers-to-pointers","02.cpp/21.pointers-to-pointers",{"title":1747,"path":1748,"stem":1749},"Оператор доступу до членів через вказівник (->)","/cpp/member-access-operator","02.cpp/22.member-access-operator",{"title":1751,"path":1752,"stem":1753},"Цикл for-each (Range-based for)","/cpp/foreach-loop","02.cpp/23.foreach-loop",{"title":1755,"path":1756,"stem":1757},"Вказівники на функції","/cpp/function-pointers","02.cpp/24.function-pointers",{"title":1759,"path":1760,"stem":1761},"Лямбда-вирази","/cpp/lambdas","02.cpp/25.lambdas",{"title":1763,"path":1764,"stem":1765},"Лямбда-захоплення","/cpp/lambda-captures","02.cpp/26.lambda-captures",{"title":1767,"path":1768,"stem":1769},"Еліпсис","/cpp/ellipsis","02.cpp/27.ellipsis",{"title":1771,"path":1772,"stem":1773},"Аргументи командного рядка","/cpp/command-line-arguments","02.cpp/28.command-line-arguments",{"title":1775,"path":1776,"stem":1777},"Перерахування (enum)","/cpp/enum","02.cpp/29.enum",{"title":1779,"path":1780,"stem":1781},"Класи-перерахування (enum class)","/cpp/enum-class","02.cpp/30.enum-class",{"title":1783,"path":1784,"stem":1785},"Псевдоніми типів (typedef і using)","/cpp/type-aliases","02.cpp/31.type-aliases",{"title":1787,"path":1788,"stem":1789},"Системи числення та двійкова арифметика","/cpp/number-systems","02.cpp/32.number-systems",{"title":1791,"path":1792,"stem":1793},"Структури (struct): агрегування даних","/cpp/struct","02.cpp/33.struct",{"title":1795,"path":1796,"stem":1797},"Структури у функціях","/cpp/struct-functions","02.cpp/34.struct-functions",{"title":1799,"path":1800,"stem":1801},"Масиви структур і вкладені структури","/cpp/struct-arrays","02.cpp/35.struct-arrays",{"title":1803,"path":1804,"stem":1805},"Патерни struct та межі застосування","/cpp/struct-patterns","02.cpp/36.struct-patterns",{"title":1807,"path":1808,"stem":1809},"План навчання: Курс C++ — Продовження (Статті 29–60+)","/cpp/curriculum-plan","02.cpp/curriculum-plan",{"title":1811,"icon":1812,"path":1813,"stem":1814,"children":1815,"page":59},"JavaScript","i-devicon-javascript","/javascript","03.javascript",[1816,1842,1896,1918,2222,2260],{"title":1817,"icon":1818,"path":1819,"stem":1820,"children":1821,"page":59},"Events","i-lucide-mouse-pointer-click","/javascript/events","03.javascript/01.events",[1822,1826,1830,1834,1838],{"title":1823,"path":1824,"stem":1825},"Вступ до подій браузера","/javascript/events/intro","03.javascript/01.events/01.intro",{"title":1827,"path":1828,"stem":1829},"Бульбашковий механізм (Bubbling) та занурення (Capturing)","/javascript/events/bubbling-capturing","03.javascript/01.events/02.bubbling-capturing",{"title":1831,"path":1832,"stem":1833},"Делегування подій (Event Delegation)","/javascript/events/delegate-events","03.javascript/01.events/03.delegate-events",{"title":1835,"path":1836,"stem":1837},"Типові дії браузера та preventDefault()","/javascript/events/prevent-default","03.javascript/01.events/04.prevent-default",{"title":1839,"path":1840,"stem":1841},"Запуск користувацьких подій (Custom Events)","/javascript/events/custom-events","03.javascript/01.events/05.custom-events",{"title":1843,"icon":1844,"path":1845,"stem":1846,"children":1847,"page":59},"Network","i-lucide-globe","/javascript/network","03.javascript/02.network",[1848,1852,1856,1860,1864,1868,1872,1876,1880,1884,1888,1892],{"title":1849,"path":1850,"stem":1851},"Fetch API - Сучасний підхід до HTTP-запитів","/javascript/network/01-fetch-api","03.javascript/02.network/01-fetch-api",{"title":1853,"path":1854,"stem":1855},"FormData - Робота з формами та файлами","/javascript/network/02-formdata","03.javascript/02.network/02-formdata",{"title":1857,"path":1858,"stem":1859},"Відстеження прогресу завантаження","/javascript/network/03-download-progress","03.javascript/02.network/03-download-progress",{"title":1861,"path":1862,"stem":1863},"Переривання fetch-запитів","/javascript/network/04-abort-requests","03.javascript/02.network/04-abort-requests",{"title":1865,"path":1866,"stem":1867},"CORS - Запити між різними джерелами","/javascript/network/05-cors","03.javascript/02.network/05-cors",{"title":1869,"path":1870,"stem":1871},"Fetch API - Повний довідник опцій","/javascript/network/06-fetch-options","03.javascript/02.network/06-fetch-options",{"title":1873,"path":1874,"stem":1875},"URL Objects - Робота з посиланнями","/javascript/network/07-url-objects","03.javascript/02.network/07-url-objects",{"title":1877,"path":1878,"stem":1879},"XMLHttpRequest - AJAX та низькорівневі запити","/javascript/network/08-xmlhttprequest","03.javascript/02.network/08-xmlhttprequest",{"title":1881,"path":1882,"stem":1883},"Відновлюване завантаження файлів","/javascript/network/09-resumable-upload","03.javascript/02.network/09-resumable-upload",{"title":1885,"path":1886,"stem":1887},"Cookies, document.cookie та світ після \"Cookiepocalypse\"","/javascript/network/10-cookies","03.javascript/02.network/10-cookies",{"title":1889,"path":1890,"stem":1891},"js-cookie: Керування Cookies без Болю","/javascript/network/11-js-cookie","03.javascript/02.network/11-js-cookie",{"title":1893,"path":1894,"stem":1895},"Axios: Потужний HTTP-клієнт для JavaScript","/javascript/network/12-axios","03.javascript/02.network/12-axios",{"title":1897,"icon":1898,"path":1899,"stem":1900,"children":1901,"page":59},"Bom","i-lucide-monitor","/javascript/bom","03.javascript/03.bom",[1902,1906,1910,1914],{"title":1903,"path":1904,"stem":1905},"LocalStorage, SessionStorage та patterns збереження даних","/javascript/bom/01-localstorage","03.javascript/03.bom/01-localstorage",{"title":1907,"path":1908,"stem":1909},"Location Object - Керування адресою сторінки","/javascript/bom/02-location-object","03.javascript/03.bom/02-location-object",{"title":1911,"path":1912,"stem":1913},"History API - Керування історією браузера","/javascript/bom/03-history-api","03.javascript/03.bom/03-history-api",{"title":1915,"path":1916,"stem":1917},"Navigator Object - Ідентифікація та Можливості Пристрою","/javascript/bom/04-navigator-object","03.javascript/03.bom/04-navigator-object",{"title":1919,"icon":1920,"path":1921,"stem":1922,"children":1923},"React","i-devicon-react","/javascript/react","03.javascript/04.react/index",[1924,1925,1929,1933,1937,1941,2004,2039,2191],{"title":1919,"path":1921,"stem":1922},{"title":1926,"path":1927,"stem":1928},"Робота з Формами в React","/javascript/react/react-forms","03.javascript/04.react/01.react-forms",{"title":1930,"path":1931,"stem":1932},"React Hook Form: Професійна Робота з Формами","/javascript/react/react-hook-form","03.javascript/04.react/02.react-hook-form",{"title":1934,"path":1935,"stem":1936},"React Hook Form: Глибоке Розуміння Архітектури та Оптимізації","/javascript/react/react-hook-form-new","03.javascript/04.react/02.react-hook-form-new",{"title":1938,"path":1939,"stem":1940},"Axios та React: Професійна Архітектура Запитів","/javascript/react/data-fetching-axios","03.javascript/04.react/03.data-fetching-axios",{"title":1942,"icon":132,"path":1943,"stem":1944,"children":1945},"Tanstack Query","/javascript/react/tanstack-query","03.javascript/04.react/04.tanstack-query/index",[1946,1948,1952,1956,1960,1964,1968,1972,1976,1980,1984,1988,1992,1996,2000],{"title":1947,"path":1943,"stem":1944},"TanStack Query: Майстерність Керування Станом Сервера",{"title":1949,"path":1950,"stem":1951},"Парадигма Server State: Чому useEffect недостатньо","/javascript/react/tanstack-query/server-state-paradigm","03.javascript/04.react/04.tanstack-query/01.server-state-paradigm",{"title":1953,"path":1954,"stem":1955},"Встановлення та Налаштування: Фундамент","/javascript/react/tanstack-query/installation-and-devtools","03.javascript/04.react/04.tanstack-query/02.installation-and-devtools",{"title":1957,"path":1958,"stem":1959},"Основи Запитів та Магія Ключів","/javascript/react/tanstack-query/query-basics-and-keys","03.javascript/04.react/04.tanstack-query/03.query-basics-and-keys",{"title":1961,"path":1962,"stem":1963},"Синхронізація Даних: Життєвий Цикл Запиту","/javascript/react/tanstack-query/data-synchronization","03.javascript/04.react/04.tanstack-query/04.data-synchronization",{"title":1965,"path":1966,"stem":1967},"Мутації та Інвалідація: Зміна Даних","/javascript/react/tanstack-query/mutations-and-invalidation","03.javascript/04.react/04.tanstack-query/05.mutations-and-invalidation",{"title":1969,"path":1970,"stem":1971},"Оптимістичні Оновлення: Швидше за Світло","/javascript/react/tanstack-query/optimistic-updates","03.javascript/04.react/04.tanstack-query/06.optimistic-updates",{"title":1973,"path":1974,"stem":1975},"Пагінація та Infinite Scroll","/javascript/react/tanstack-query/pagination-and-load-more","03.javascript/04.react/04.tanstack-query/07.pagination-and-load-more",{"title":1977,"path":1978,"stem":1979},"Просунуті Патерни та Оптимізація","/javascript/react/tanstack-query/advanced-patterns","03.javascript/04.react/04.tanstack-query/08.advanced-patterns",{"title":1981,"path":1982,"stem":1983},"Архітектура та Best Practices","/javascript/react/tanstack-query/architecture-and-best-practices","03.javascript/04.react/04.tanstack-query/09.architecture-and-best-practices",{"title":1985,"path":1986,"stem":1987},"Server-Side Rendering (SSR) та Гідратація","/javascript/react/tanstack-query/server-side-rendering","03.javascript/04.react/04.tanstack-query/10.server-side-rendering",{"title":1989,"path":1990,"stem":1991},"Стратегії Тестування","/javascript/react/tanstack-query/testing-strategies","03.javascript/04.react/04.tanstack-query/11.testing-strategies",{"title":1993,"path":1994,"stem":1995},"Аутентифікація та Обробка Помилок","/javascript/react/tanstack-query/authentication-and-errors","03.javascript/04.react/04.tanstack-query/12.authentication-and-errors",{"title":1997,"path":1998,"stem":1999},"React Suspense та Майбутнє","/javascript/react/tanstack-query/react-suspense","03.javascript/04.react/04.tanstack-query/13.react-suspense",{"title":2001,"path":2002,"stem":2003},"Глибоке Занурення в Продуктивність","/javascript/react/tanstack-query/performance-deep-dive","03.javascript/04.react/04.tanstack-query/14.performance-deep-dive",{"title":2005,"icon":1920,"path":2006,"stem":2007,"children":2008},"React Router","/javascript/react/react-router","03.javascript/04.react/05.react-router/index",[2009,2011,2015,2019,2023,2027,2031,2035],{"title":2010,"path":2006,"stem":2007},"React Router: Навігаційна система сучасного вебу",{"title":2012,"path":2013,"stem":2014},"Налаштування та Базовий Роутинг","/javascript/react/react-router/setup-and-basic-routing","03.javascript/04.react/05.react-router/01.setup-and-basic-routing",{"title":2016,"path":2017,"stem":2018},"Динамічна Навігація","/javascript/react/react-router/navigation-and-links","03.javascript/04.react/05.react-router/02.navigation-and-links",{"title":2020,"path":2021,"stem":2022},"Вкладені Маршрути та Макети","/javascript/react/react-router/nested-routes-and-layouts","03.javascript/04.react/05.react-router/03.nested-routes-and-layouts",{"title":2024,"path":2025,"stem":2026},"Динамічні Маршрути та Параметри","/javascript/react/react-router/dynamic-routing","03.javascript/04.react/05.react-router/04.dynamic-routing",{"title":2028,"path":2029,"stem":2030},"Data APIs: Loaders та Actions","/javascript/react/react-router/data-loading","03.javascript/04.react/05.react-router/05.data-loading",{"title":2032,"path":2033,"stem":2034},"Просунуті Патерни","/javascript/react/react-router/advanced-patterns","03.javascript/04.react/05.react-router/06.advanced-patterns",{"title":2036,"path":2037,"stem":2038},"Legacy Routing: Компонентний підхід","/javascript/react/react-router/legacy-routing","03.javascript/04.react/05.react-router/07.legacy-routing",{"title":2040,"icon":132,"path":2041,"stem":2042,"children":2043},"Redux","/javascript/react/redux","03.javascript/04.react/06.redux/index",[2044,2046,2062,2091,2100,2121,2137,2166],{"title":2045,"path":2041,"stem":2042},"Redux: Еволюція управління станом",{"title":14,"icon":15,"path":2047,"stem":2048,"children":2049,"page":59},"/javascript/react/redux/fundamentals","03.javascript/04.react/06.redux/01.fundamentals",[2050,2054,2058],{"title":2051,"path":2052,"stem":2053},"Вступ до State Management","/javascript/react/redux/fundamentals/intro-state-management","03.javascript/04.react/06.redux/01.fundamentals/01.intro-state-management",{"title":2055,"path":2056,"stem":2057},"Філософія Redux та Три Принципи","/javascript/react/redux/fundamentals/redux-philosophy","03.javascript/04.react/06.redux/01.fundamentals/02.redux-philosophy",{"title":2059,"path":2060,"stem":2061},"Чисті функції та Іммутабельність","/javascript/react/redux/fundamentals/pure-functions-immutability","03.javascript/04.react/06.redux/01.fundamentals/03.pure-functions-immutability",{"title":2063,"icon":132,"path":2064,"stem":2065,"children":2066,"page":59},"Classic Redux","/javascript/react/redux/classic-redux","03.javascript/04.react/06.redux/02.classic-redux",[2067,2071,2075,2079,2083,2087],{"title":2068,"path":2069,"stem":2070},"Створення Store (Classic Redux)","/javascript/react/redux/classic-redux/store-setup","03.javascript/04.react/06.redux/02.classic-redux/01.store-setup",{"title":2072,"path":2073,"stem":2074},"Actions, Constants та Action Creators","/javascript/react/redux/classic-redux/actions-constants","03.javascript/04.react/06.redux/02.classic-redux/02.actions-constants",{"title":2076,"path":2077,"stem":2078},"Логіка Reducers","/javascript/react/redux/classic-redux/reducers","03.javascript/04.react/06.redux/02.classic-redux/03.reducers",{"title":2080,"path":2081,"stem":2082},"Комбінування Reducers (Root Reducer)","/javascript/react/redux/classic-redux/data-flow","03.javascript/04.react/06.redux/02.classic-redux/04.data-flow",{"title":2084,"path":2085,"stem":2086},"Підключення до React (React-Redux)","/javascript/react/redux/classic-redux/react-redux-connection","03.javascript/04.react/06.redux/02.classic-redux/05.react-redux-connection",{"title":2088,"path":2089,"stem":2090},"Middleware та Асинхронність (Redux Thunk)","/javascript/react/redux/classic-redux/middleware-thunk","03.javascript/04.react/06.redux/02.classic-redux/06.middleware-thunk",{"title":2092,"icon":132,"path":2093,"stem":2094,"children":2095,"page":59},"Transition To Rtk","/javascript/react/redux/transition-to-rtk","03.javascript/04.react/06.redux/03.transition-to-rtk",[2096],{"title":2097,"path":2098,"stem":2099},"Проблеми класичного Redux","/javascript/react/redux/transition-to-rtk/problems-with-classic","03.javascript/04.react/06.redux/03.transition-to-rtk/01.problems-with-classic",{"title":2101,"icon":132,"path":2102,"stem":2103,"children":2104,"page":59},"Redux Toolkit","/javascript/react/redux/redux-toolkit","03.javascript/04.react/06.redux/04.redux-toolkit",[2105,2109,2113,2117],{"title":2106,"path":2107,"stem":2108},"Налаштування Store з configureStore","/javascript/react/redux/redux-toolkit/configure-store","03.javascript/04.react/06.redux/04.redux-toolkit/01.configure-store",{"title":2110,"path":2111,"stem":2112},"createSlice: Революція в Redux","/javascript/react/redux/redux-toolkit/create-slice","03.javascript/04.react/06.redux/04.redux-toolkit/02.create-slice",{"title":2114,"path":2115,"stem":2116},"Асинхронність з createAsyncThunk","/javascript/react/redux/redux-toolkit/async-thunks","03.javascript/04.react/06.redux/04.redux-toolkit/03.async-thunks",{"title":2118,"path":2119,"stem":2120},"04. Entity Adapter: Керування нормалізованим станом","/javascript/react/redux/redux-toolkit/entity-adapter","03.javascript/04.react/06.redux/04.redux-toolkit/04.entity-adapter",{"title":2122,"icon":92,"path":2123,"stem":2124,"children":2125,"page":59},"Advanced","/javascript/react/redux/advanced","03.javascript/04.react/06.redux/05.advanced",[2126,2130,2134],{"title":2127,"path":2128,"stem":2129},"Мемоізація та Селектори: Повний Гайд по Reselect","/javascript/react/redux/advanced/selectors-reselect","03.javascript/04.react/06.redux/05.advanced/01.selectors-reselect",{"title":2131,"path":2132,"stem":2133},"RTK Query: Архітектура Серверного Кешу","/javascript/react/redux/advanced/rtk-query-intro","03.javascript/04.react/06.redux/05.advanced/02.rtk-query-intro",{"title":1981,"path":2135,"stem":2136},"/javascript/react/redux/advanced/architecture-best-practices","03.javascript/04.react/06.redux/05.advanced/03.architecture-best-practices",{"title":2138,"icon":132,"path":2139,"stem":2140,"children":2141,"page":59},"Project Kanban","/javascript/react/redux/project-kanban","03.javascript/04.react/06.redux/06.project-kanban",[2142,2146,2150,2154,2158,2162],{"title":2143,"path":2144,"stem":2145},"Проєкт: Kanban Board (Trello Clone)","/javascript/react/redux/project-kanban/project-overview","03.javascript/04.react/06.redux/06.project-kanban/01.project-overview",{"title":2147,"path":2148,"stem":2149},"Налаштування та Типізація","/javascript/react/redux/project-kanban/setup-and-types","03.javascript/04.react/06.redux/06.project-kanban/02.setup-and-types",{"title":2151,"path":2152,"stem":2153},"Board Slice: Серце Дошки","/javascript/react/redux/project-kanban/board-slice","03.javascript/04.react/06.redux/06.project-kanban/03.board-slice",{"title":2155,"path":2156,"stem":2157},"Логіка Drag & Drop","/javascript/react/redux/project-kanban/drag-and-drop-logic","03.javascript/04.react/06.redux/06.project-kanban/04.drag-and-drop-logic",{"title":2159,"path":2160,"stem":2161},"Інтеграція з RTK Query","/javascript/react/redux/project-kanban/rtk-query-integration","03.javascript/04.react/06.redux/06.project-kanban/05.rtk-query-integration",{"title":2163,"path":2164,"stem":2165},"Optimistic Updates","/javascript/react/redux/project-kanban/optimistic-updates","03.javascript/04.react/06.redux/06.project-kanban/06.optimistic-updates",{"title":2167,"icon":132,"path":2168,"stem":2169,"children":2170,"page":59},"Testing","/javascript/react/redux/testing","03.javascript/04.react/06.redux/07.testing",[2171,2175,2179,2183,2187],{"title":2172,"path":2173,"stem":2174},"Тестування Redux","/javascript/react/redux/testing/intro-testing","03.javascript/04.react/06.redux/07.testing/01.intro-testing",{"title":2176,"path":2177,"stem":2178},"Тестування Reducers","/javascript/react/redux/testing/testing-reducers","03.javascript/04.react/06.redux/07.testing/02.testing-reducers",{"title":2180,"path":2181,"stem":2182},"Тестування Селекторів","/javascript/react/redux/testing/testing-selectors","03.javascript/04.react/06.redux/07.testing/03.testing-selectors",{"title":2184,"path":2185,"stem":2186},"Тестування Компонентів (Integration)","/javascript/react/redux/testing/testing-components","03.javascript/04.react/06.redux/07.testing/04.testing-components",{"title":2188,"path":2189,"stem":2190},"Тестування Async Thunks","/javascript/react/redux/testing/testing-thunks","03.javascript/04.react/06.redux/07.testing/05.testing-thunks",{"title":2192,"icon":132,"path":2193,"stem":2194,"children":2195},"Ui Libraries","/javascript/react/ui-libraries","03.javascript/04.react/07.ui-libraries/index",[2196,2198,2202,2206,2210,2214,2218],{"title":2197,"path":2193,"stem":2194},"UI Бібліотеки в React",{"title":2199,"path":2200,"stem":2201},"Вступ до UI Бібліотек: Навіщо Винаходити Велосипед Двічі?","/javascript/react/ui-libraries/introduction-to-ui-libraries","03.javascript/04.react/07.ui-libraries/01.introduction-to-ui-libraries",{"title":2203,"path":2204,"stem":2205},"Філософія shadcn/ui: \"Not a Component Library\"","/javascript/react/ui-libraries/shadcn-philosophy","03.javascript/04.react/07.ui-libraries/02.shadcn-philosophy",{"title":2207,"path":2208,"stem":2209},"Установка та Налаштування shadcn/ui","/javascript/react/ui-libraries/shadcn-installation","03.javascript/04.react/07.ui-libraries/03.shadcn-installation",{"title":2211,"path":2212,"stem":2213},"Базові Компоненти shadcn/ui: Фундамент Інтерфейсу","/javascript/react/ui-libraries/shadcn-components-basics","03.javascript/04.react/07.ui-libraries/04.shadcn-components-basics",{"title":2215,"path":2216,"stem":2217},"Компоненти Форм: Побудова Інтерактивних Form","/javascript/react/ui-libraries/shadcn-components-forms","03.javascript/04.react/07.ui-libraries/05.shadcn-components-forms",{"title":2219,"path":2220,"stem":2221},"Складні Компоненти: Dialog, Dropdown, Table та Command","/javascript/react/ui-libraries/shadcn-components-advanced","03.javascript/04.react/07.ui-libraries/06.shadcn-components-advanced",{"title":2223,"icon":2224,"path":2225,"stem":2226,"children":2227,"page":59},"TypeScript","i-devicon-typescript","/javascript/typescript","03.javascript/05.typescript",[2228,2232,2236,2240,2244,2248,2252,2256],{"title":2229,"path":2230,"stem":2231},"TypeScript: Броня для вашого коду","/javascript/typescript/intro-and-basic-types","03.javascript/05.typescript/01.intro-and-basic-types",{"title":2233,"path":2234,"stem":2235},"Майстерність Моделювання Даних: Інтерфейси та Просунуті Типи","/javascript/typescript/interfaces-and-advanced-types","03.javascript/05.typescript/02.interfaces-and-advanced-types",{"title":2237,"path":2238,"stem":2239},"Алхімія Типів: Generics та Utility Types","/javascript/typescript/generics-and-utilities","03.javascript/05.typescript/03.generics-and-utilities",{"title":2241,"path":2242,"stem":2243},"Архітектура та Шаблони: Класи в TypeScript","/javascript/typescript/classes-and-oop","03.javascript/05.typescript/04.classes-and-oop",{"title":2245,"path":2246,"stem":2247},"Продакшн та Екосистема: Advanced Config & Workflow","/javascript/typescript/advanced-patterns-and-config","03.javascript/05.typescript/05.advanced-patterns-and-config",{"title":2249,"path":2250,"stem":2251},"TypeScript у світі React","/javascript/typescript/react-basics","03.javascript/05.typescript/06.react-basics",{"title":2253,"path":2254,"stem":2255},"React + TypeScript: Продвинуті патерни","/javascript/typescript/react-advanced","03.javascript/05.typescript/07.react-advanced",{"title":2257,"path":2258,"stem":2259},"React + TypeScript: Екосистема та бібліотеки","/javascript/typescript/react-ecosystem","03.javascript/05.typescript/08.react-ecosystem",{"title":2261,"path":2262,"stem":2263},"Atomic Design","/javascript/atomic-design","03.javascript/2.atomic-design",{"title":2265,"icon":2266,"path":2267,"stem":2268,"children":2269,"page":59},"Java","i-devicon-java","/java","04.java",[2270,2273,2276,2280,2284,2288,2292],{"title":162,"path":2271,"stem":2272},"/java/data-mapper-part1","04.java/01.data-mapper-part1",{"title":166,"path":2274,"stem":2275},"/java/data-mapper-part2","04.java/02.data-mapper-part2",{"title":2277,"path":2278,"stem":2279},"Service Layer: Організація бізнес-логіки","/java/service-layer","04.java/03.service-layer",{"title":2281,"path":2282,"stem":2283},"Rich Domain Model та State Pattern","/java/rich-domain-model","04.java/04.rich-domain-model",{"title":2285,"path":2286,"stem":2287},"Патерни для складної бізнес-логіки","/java/business-logic-patterns","04.java/05.business-logic-patterns",{"title":2289,"path":2290,"stem":2291},"Обробка помилок та валідація","/java/error-handling-validation","04.java/06.error-handling-validation",{"title":2293,"path":2294,"stem":2295,"children":2296,"page":59},"Проектування баз даних","/java/pr2","04.java/pr2",[2297,2301,2305,2309,2313,2317,2321,2325,2329,2333,2337,2341,2345,2349,2353,2357,2361,2365,2369,2373,2377,2381,2385,2389,2393,2397,2401,2405,2409,2413,2417,2421,2425,2429,2433,2437,2441],{"title":2298,"path":2299,"stem":2300},"Концептуальне моделювання: Мистецтво розуміння предметної області","/java/pr2/conceptual-modeling","04.java/pr2/01.conceptual-modeling",{"title":2302,"path":2303,"stem":2304},"Логічне моделювання: Від бізнес-ідей до структур даних","/java/pr2/logical-modeling","04.java/pr2/02.logical-modeling",{"title":2306,"path":2307,"stem":2308},"Нормалізація: Гігієна даних та боротьба з аномаліями","/java/pr2/normalization","04.java/pr2/03.normalization",{"title":2310,"path":2311,"stem":2312},"Фізична схема: Від абстракції до DDL","/java/pr2/physical-schema","04.java/pr2/04.physical-schema",{"title":2314,"path":2315,"stem":2316},"Архітектурна класифікація таблиць","/java/pr2/table-classification","04.java/pr2/05.table-classification",{"title":2318,"path":2319,"stem":2320},"Database Migrations: Версіонування схеми з Flyway","/java/pr2/database-migrations","04.java/pr2/06.database-migrations",{"title":2322,"path":2323,"stem":2324},"А що, якби це була не реляційна БД?","/java/pr2/beyond-relational","04.java/pr2/07.beyond-relational",{"title":2326,"path":2327,"stem":2328},"Object-Relational Impedance Mismatch: Два світи, що не хочуть дружити","/java/pr2/impedance-mismatch","04.java/pr2/09.impedance-mismatch",{"title":2330,"path":2331,"stem":2332},"JDBC: Перший контакт із базою даних","/java/pr2/jdbc-fundamentals","04.java/pr2/10.jdbc-fundamentals",{"title":2334,"path":2335,"stem":2336},"Якість коду: Spotless, SpotBugs та SonarQube","/java/pr2/10a.code-quality","04.java/pr2/10a.code-quality",{"title":2338,"path":2339,"stem":2340},"Connection Pool: Патерн Object Pool для JDBC-з'єднань","/java/pr2/connection-pool","04.java/pr2/11.connection-pool",{"title":2342,"path":2343,"stem":2344},"Row Data Gateway: Об'єкт як обгортка рядка таблиці","/java/pr2/row-data-gateway","04.java/pr2/12.row-data-gateway",{"title":2346,"path":2347,"stem":2348},"Table Data Gateway: Фасад таблиці як архітектурний відступ","/java/pr2/table-data-gateway","04.java/pr2/13.table-data-gateway",{"title":2350,"path":2351,"stem":2352},"Repository + Data Mapper: Правильна шарова архітектура з JDBC","/java/pr2/repository-data-mapper","04.java/pr2/14.repository-data-mapper",{"title":2354,"path":2355,"stem":2356},"Identity Map: Кешування сутностей у рамках сесії","/java/pr2/identity-map","04.java/pr2/15.identity-map",{"title":2358,"path":2359,"stem":2360},"Unit of Work: Відстеження змін і координація JDBC-транзакцій","/java/pr2/unit-of-work","04.java/pr2/16.unit-of-work",{"title":2362,"path":2363,"stem":2364},"Strategy: Замінювані SQL-стратегії для підтримки різних СУБД","/java/pr2/strategy-sql","04.java/pr2/17.strategy-sql",{"title":2366,"path":2367,"stem":2368},"Proxy: Lazy Loading для One-To-Many колекцій","/java/pr2/proxy-lazy-loading","04.java/pr2/18.proxy-lazy-loading",{"title":2370,"path":2371,"stem":2372},"Generic Repository через Java Reflection: анотації та динамічний SQL","/java/pr2/generic-repository-reflection","04.java/pr2/19.generic-repository-reflection",{"title":2374,"path":2375,"stem":2376},"Specification Pattern: Композиція бізнес-правил для складних запитів","/java/pr2/specification-pattern","04.java/pr2/20.specification-pattern",{"title":2378,"path":2379,"stem":2380},"Розширені можливості Specification Pattern: підзапити, агрегації та гібридний підхід","/java/pr2/20a.advanced-specifications","04.java/pr2/20a.advanced-specifications",{"title":2382,"path":2383,"stem":2384},"Асинхронність у JDBC: Від блокуючих викликів до CompletableFuture","/java/pr2/asynchronous-jdbc","04.java/pr2/21.asynchronous-jdbc",{"title":2386,"path":2387,"stem":2388},"Інтеграційне тестування JDBC-репозиторіїв: Embedded H2 та патерн AAA","/java/pr2/integration-testing-h2","04.java/pr2/22.integration-testing-h2",{"title":2390,"path":2391,"stem":2392},"Testcontainers: Тестування з реальною PostgreSQL у Docker-контейнерах","/java/pr2/integration-testing-testcontainers","04.java/pr2/23.integration-testing-testcontainers",{"title":2394,"path":2395,"stem":2396},"Google Guice: Впровадження залежностей у JavaFX-проєкті","/java/pr2/dependency-injection-guice","04.java/pr2/24.dependency-injection-guice",{"title":2398,"path":2399,"stem":2400},"JavaFX: Основи побудови графічних інтерфейсів","/java/pr2/javafx-fundamentals","04.java/pr2/25.javafx-fundamentals",{"title":2402,"path":2403,"stem":2404},"Properties та Bindings: Реактивність у JavaFX","/java/pr2/javafx-properties-bindings","04.java/pr2/26.javafx-properties-bindings",{"title":2406,"path":2407,"stem":2408},"MVC vs MVP vs MVVM: Еволюція архітектурних патернів UI","/java/pr2/ui-architecture-patterns","04.java/pr2/27.ui-architecture-patterns",{"title":2410,"path":2411,"stem":2412},"MVVM на практиці: Побудова ViewModel","/java/pr2/mvvm-viewmodel-implementation","04.java/pr2/28.mvvm-viewmodel-implementation",{"title":2414,"path":2415,"stem":2416},"View та Controller: Зв'язування з ViewModel через FXML","/java/pr2/mvvm-view-controller","04.java/pr2/29.mvvm-view-controller",{"title":2418,"path":2419,"stem":2420},"Інтеграція MVVM з Guice: Автоматична ін'єкція залежностей","/java/pr2/mvvm-guice-integration","04.java/pr2/30.mvvm-guice-integration",{"title":2422,"path":2423,"stem":2424},"Валідація та обробка помилок у MVVM","/java/pr2/mvvm-validation-error-handling","04.java/pr2/31.mvvm-validation-error-handling",{"title":2426,"path":2427,"stem":2428},"Навігація та управління екранами у JavaFX MVVM","/java/pr2/mvvm-navigation-screen-management","04.java/pr2/32.mvvm-navigation-screen-management",{"title":2430,"path":2431,"stem":2432},"Тестування JavaFX MVVM-додатків","/java/pr2/mvvm-testing","04.java/pr2/33.mvvm-testing",{"title":2434,"path":2435,"stem":2436},"Стилізація та теми у JavaFX: CSS та User Experience","/java/pr2/javafx-styling-themes","04.java/pr2/34.javafx-styling-themes",{"title":2438,"path":2439,"stem":2440},"AtlantaFX: Сучасні теми для JavaFX додатків","/java/pr2/atlantafx-modern-themes","04.java/pr2/35.atlantafx-modern-themes",{"title":2442,"path":2443,"stem":2444},"Пакування та розповсюдження JavaFX-додатків","/java/pr2/jar-packaging-distribution","04.java/pr2/36.jar-packaging-distribution",{"title":2446,"icon":2447,"path":2448,"stem":2449,"children":2450,"page":59},"Бази даних","i-lucide-database","/databases","06.databases",[2451,2481,2504,2541,2570,2588,2622,2634,2643],{"title":2452,"icon":2453,"path":2454,"stem":2455,"children":2456,"page":59},"Intro","i-lucide-play","/databases/intro","06.databases/01.intro",[2457,2461,2465,2469,2473,2477],{"title":2458,"path":2459,"stem":2460},"Введення в теорію баз даних","/databases/intro/introduction-to-databases","06.databases/01.intro/01.introduction-to-databases",{"title":2462,"path":2463,"stem":2464},"Реляційна модель даних","/databases/intro/relational-model-theory","06.databases/01.intro/02.relational-model-theory",{"title":2466,"path":2467,"stem":2468},"ER-моделювання","/databases/intro/er-modeling","06.databases/01.intro/03.er-modeling",{"title":2470,"path":2471,"stem":2472},"Логічне проектування БД","/databases/intro/logical-schema","06.databases/01.intro/04.logical-schema",{"title":2474,"path":2475,"stem":2476},"Класифікація таблиць","/databases/intro/table-classification","06.databases/01.intro/05.table-classification",{"title":2478,"path":2479,"stem":2480},"PlantUML для баз даних","/databases/intro/plantuml-diagrams","06.databases/01.intro/06.plantuml-diagrams",{"title":2482,"icon":2447,"path":2483,"stem":2484,"children":2485,"page":59},"MS SQL Server Start","/databases/ms-sql-server-start","06.databases/02.ms-sql-server-start",[2486,2490,2496,2500],{"title":2487,"path":2488,"stem":2489},"Типи даних у MS SQL Server","/databases/ms-sql-server-start/data-types","06.databases/02.ms-sql-server-start/01.data-types",{"title":2491,"path":2492,"stem":2493,"children":2494},"Індекси у MS SQL Server","/databases/ms-sql-server-start/sql-indexes","06.databases/02.ms-sql-server-start/02.sql-indexes",[2495],{"title":2491,"path":2492,"stem":2493},{"title":2497,"path":2498,"stem":2499},"Системні бази даних MS SQL Server","/databases/ms-sql-server-start/system-databases","06.databases/02.ms-sql-server-start/03.system-databases",{"title":2501,"path":2502,"stem":2503},"Огляд мови SQL та запитів","/databases/ms-sql-server-start/sql-queries-overview","06.databases/02.ms-sql-server-start/04.sql-queries-overview",{"title":2505,"icon":2447,"path":2506,"stem":2507,"children":2508,"page":59},"SQL","/databases/sql","06.databases/03.sql",[2509,2513,2517,2521,2525,2529,2533,2537],{"title":2510,"path":2511,"stem":2512},"Налаштування демонстраційної бази даних","/databases/sql/sample-database-setup","06.databases/03.sql/00.sample-database-setup",{"title":2514,"path":2515,"stem":2516},"DDL - Створення таблиць (CREATE TABLE)","/databases/sql/ddl-create-table","06.databases/03.sql/01.ddl-create-table",{"title":2518,"path":2519,"stem":2520},"DDL - Зміна та видалення таблиць (ALTER, DROP)","/databases/sql/ddl-alter-drop-table","06.databases/03.sql/02.ddl-alter-drop-table",{"title":2522,"path":2523,"stem":2524},"SELECT запити - Основи","/databases/sql/select-queries-fundamentals","06.databases/03.sql/03.select-queries-fundamentals",{"title":2526,"path":2527,"stem":2528},"SELECT запити - Розширені можливості","/databases/sql/select-queries-advanced","06.databases/03.sql/04.select-queries-advanced",{"title":2530,"path":2531,"stem":2532},"INSERT запити - Додавання даних","/databases/sql/insert-queries","06.databases/03.sql/05.insert-queries",{"title":2534,"path":2535,"stem":2536},"UPDATE та DELETE запити","/databases/sql/update-delete-queries","06.databases/03.sql/06.update-delete-queries",{"title":2538,"path":2539,"stem":2540},"Транзакції в SQL","/databases/sql/transactions","06.databases/03.sql/07.transactions",{"title":2542,"icon":2447,"path":2543,"stem":2544,"children":2545,"page":59},"Multi Table Databases","/databases/multi-table-databases","06.databases/04.multi-table-databases",[2546,2550,2554,2558,2562,2566],{"title":2547,"path":2548,"stem":2549},"Зв'язки та нормалізація БД","/databases/multi-table-databases/relationships-and-normalization","06.databases/04.multi-table-databases/00.relationships-and-normalization",{"title":2551,"path":2552,"stem":2553},"INNER JOIN - З'єднання таблиць","/databases/multi-table-databases/inner-join","06.databases/04.multi-table-databases/01.inner-join",{"title":2555,"path":2556,"stem":2557},"OUTER JOINs - LEFT, RIGHT, FULL","/databases/multi-table-databases/outer-joins","06.databases/04.multi-table-databases/02.outer-joins",{"title":2559,"path":2560,"stem":2561},"CROSS та SELF JOINs","/databases/multi-table-databases/cross-self-joins","06.databases/04.multi-table-databases/03.cross-self-joins",{"title":2563,"path":2564,"stem":2565},"Підзапити (Subqueries)","/databases/multi-table-databases/subqueries","06.databases/04.multi-table-databases/04.subqueries",{"title":2567,"path":2568,"stem":2569},"Агрегації з JOIN","/databases/multi-table-databases/aggregations-with-joins","06.databases/04.multi-table-databases/05.aggregations-with-joins",{"title":2571,"icon":2572,"path":2573,"stem":2574,"children":2575,"page":59},"Aggregate Functions","i-lucide-calculator","/databases/aggregate-functions","06.databases/05.aggregate-functions",[2576,2580,2584],{"title":2577,"path":2578,"stem":2579},"Функції агрегування в MS SQL Server","/databases/aggregate-functions/introduction-aggregate-functions","06.databases/05.aggregate-functions/01.introduction-aggregate-functions",{"title":2581,"path":2582,"stem":2583},"Групування даних в MS SQL Server","/databases/aggregate-functions/grouping-data","06.databases/05.aggregate-functions/02.grouping-data",{"title":2585,"path":2586,"stem":2587},"Підзапити з агрегатними функціями","/databases/aggregate-functions/subqueries-aggregates","06.databases/05.aggregate-functions/03.subqueries-aggregates",{"title":2589,"icon":2590,"path":2591,"stem":2592,"children":2593,"page":59},"Тригери та зберігаємі процедури","i-lucide-database-zap","/databases/triggers-stored-procedures","06.databases/07.triggers-stored-procedures",[2594,2598,2602,2606,2610,2614,2618],{"title":2595,"path":2596,"stem":2597},"DML-тригери","/databases/triggers-stored-procedures/dml-triggers","06.databases/07.triggers-stored-procedures/01.dml-triggers",{"title":2599,"path":2600,"stem":2601},"DDL-тригери","/databases/triggers-stored-procedures/ddl-triggers","06.databases/07.triggers-stored-procedures/02.ddl-triggers",{"title":2603,"path":2604,"stem":2605},"Transact-SQL розширення","/databases/triggers-stored-procedures/transact-sql-extensions","06.databases/07.triggers-stored-procedures/03.transact-sql-extensions",{"title":2607,"path":2608,"stem":2609},"Транзакції","/databases/triggers-stored-procedures/transactions","06.databases/07.triggers-stored-procedures/04.transactions",{"title":2611,"path":2612,"stem":2613},"Зберігаємі процедури","/databases/triggers-stored-procedures/stored-procedures","06.databases/07.triggers-stored-procedures/05.stored-procedures",{"title":2615,"path":2616,"stem":2617},"Користувацькі функції","/databases/triggers-stored-procedures/user-defined-functions","06.databases/07.triggers-stored-procedures/06.user-defined-functions",{"title":2619,"path":2620,"stem":2621},"Безпека баз даних","/databases/triggers-stored-procedures/security","06.databases/07.triggers-stored-procedures/08.security",{"title":2619,"icon":793,"path":2623,"stem":2624,"children":2625,"page":59},"/databases/security","06.databases/08.security",[2626,2630],{"title":2627,"path":2628,"stem":2629},"Вступ до безпеки баз даних","/databases/security/introduction","06.databases/08.security/01.introduction",{"title":2631,"path":2632,"stem":2633},"Системні представлення та метадані","/databases/security/system-views","06.databases/08.security/02.system-views",{"title":2635,"icon":2636,"path":2637,"stem":2638,"children":2639,"page":59},"Резервне копіювання та відновлення","i-lucide-database-backup","/databases/backup-recovery","06.databases/09.backup-recovery",[2640],{"title":2635,"path":2641,"stem":2642},"/databases/backup-recovery/backup-restore","06.databases/09.backup-recovery/01.backup-restore",{"title":2644,"icon":2645,"path":2646,"stem":2647,"children":2648,"page":59},"Повнотекстовий пошук","i-lucide-search","/databases/full-text-search","06.databases/10.full-text-search",[2649],{"title":2644,"path":2650,"stem":2651},"/databases/full-text-search/full-text-search","06.databases/10.full-text-search/01.full-text-search",{"title":2653,"icon":2654,"path":2655,"stem":2656,"children":2657,"page":59},"Tools","i-lucide-wrench","/tools","07.tools",[2658,2734],{"title":2659,"icon":2660,"path":2661,"stem":2662,"children":2663},"Docker","i-simple-icons-docker","/tools/docker","07.tools/01.docker/index",[2664,2666,2670,2674,2678,2682,2686,2690,2694,2698,2702,2706,2710,2714,2718,2722,2726,2730],{"title":2665,"path":2661,"stem":2662},"Docker: від нуля до production",{"title":2667,"path":2668,"stem":2669},"Контейнеризація — від проблеми до рішення","/tools/docker/containerization-concept","07.tools/01.docker/01.containerization-concept",{"title":2671,"path":2672,"stem":2673},"Docker — що це і навіщо?","/tools/docker/docker-what-and-why","07.tools/01.docker/02.docker-what-and-why",{"title":2675,"path":2676,"stem":2677},"Архітектура Docker Engine","/tools/docker/docker-architecture","07.tools/01.docker/03.docker-architecture",{"title":2679,"path":2680,"stem":2681},"Встановлення Docker","/tools/docker/installation","07.tools/01.docker/04.installation",{"title":2683,"path":2684,"stem":2685},"Перший контейнер — docker run","/tools/docker/first-container","07.tools/01.docker/05.first-container",{"title":2687,"path":2688,"stem":2689},"Життєвий цикл контейнера","/tools/docker/container-lifecycle","07.tools/01.docker/06.container-lifecycle",{"title":2691,"path":2692,"stem":2693},"Docker Images — фундаментальні концепції","/tools/docker/docker-images-fundamentals","07.tools/01.docker/07.docker-images-fundamentals",{"title":2695,"path":2696,"stem":2697},"Dockerfile — основи","/tools/docker/dockerfile-basics","07.tools/01.docker/08.dockerfile-basics",{"title":2699,"path":2700,"stem":2701},"Dockerfile — просунуті техніки","/tools/docker/dockerfile-advanced","07.tools/01.docker/09.dockerfile-advanced",{"title":2703,"path":2704,"stem":2705},"Build Context та кешування шарів","/tools/docker/build-context-and-cache","07.tools/01.docker/10.build-context-and-cache",{"title":2707,"path":2708,"stem":2709},"Реєстри Docker-образів","/tools/docker/image-registries","07.tools/01.docker/11.image-registries",{"title":2711,"path":2712,"stem":2713},"Контейнеризація .NET додатків","/tools/docker/dotnet-containerization","07.tools/01.docker/12.dotnet-containerization",{"title":2715,"path":2716,"stem":2717},"Томи та збереження даних","/tools/docker/volumes-and-data","07.tools/01.docker/13.volumes-and-data",{"title":2719,"path":2720,"stem":2721},"Основи мережі в Docker","/tools/docker/networking-basics","07.tools/01.docker/14.networking-basics",{"title":2723,"path":2724,"stem":2725},"Змінні оточення та конфігурація","/tools/docker/environment-and-configuration","07.tools/01.docker/15.environment-and-configuration",{"title":2727,"path":2728,"stem":2729},"Docker Compose — оркестрація контейнерів","/tools/docker/docker-compose-basics","07.tools/01.docker/16.docker-compose-basics",{"title":2731,"path":2732,"stem":2733},"Docker Compose — Multi-Service застосунки","/tools/docker/compose-multi-service","07.tools/01.docker/17.compose-multi-service",{"title":2735,"icon":2736,"path":2737,"stem":2738,"children":2739},"Kubernetes","simple-icons:kubernetes","/tools/kubernetes","07.tools/02.kubernetes/index",[2740,2742,2746,2750,2754,2758,2762,2766,2770],{"title":2741,"path":2737,"stem":2738},"Kubernetes: від розробки до production",{"title":2743,"path":2744,"stem":2745},"Kubernetes — коли Docker Compose більше не вистачає","/tools/kubernetes/why-kubernetes","07.tools/02.kubernetes/01.why-kubernetes",{"title":2747,"path":2748,"stem":2749},"Архітектура Kubernetes — анатомія кластера","/tools/kubernetes/kubernetes-architecture","07.tools/02.kubernetes/02.kubernetes-architecture",{"title":2751,"path":2752,"stem":2753},"Локальне середовище — minikube, kind та k3s","/tools/kubernetes/local-environment","07.tools/02.kubernetes/03.local-environment",{"title":2755,"path":2756,"stem":2757},"Pod — атомарна одиниця Kubernetes","/tools/kubernetes/pods-and-containers","07.tools/02.kubernetes/04.pods-and-containers",{"title":2759,"path":2760,"stem":2761},"Патерни використання Pod","/tools/kubernetes/pod-patterns","07.tools/02.kubernetes/05.pod-patterns",{"title":2763,"path":2764,"stem":2765},"Deployment — декларативне управління Pod","/tools/kubernetes/deployment-basics","07.tools/02.kubernetes/06.deployment-basics",{"title":2767,"path":2768,"stem":2769},"Rolling Updates та управління життєвим циклом Deployment","/tools/kubernetes/deployment-rolling-updates","07.tools/02.kubernetes/07.deployment-rolling-updates",{"title":2771,"path":2772,"stem":2773},"Service — мережева абстракція для Pod","/tools/kubernetes/services-networking","07.tools/02.kubernetes/08.services-networking",{"title":2775,"icon":2776,"path":2777,"stem":2778,"children":2779,"page":59},"Software Engineering","i-lucide-code-2","/software-engineering","09.software-engineering",[2780,2784,2788,2792,2796,2800,2804,2808,2812,2816,2820],{"title":2781,"path":2782,"stem":2783},"1. Аналіз предметної області. Експертні знання та складність","/software-engineering/intro-subdomains","09.software-engineering/01.intro-subdomains",{"title":2785,"path":2786,"stem":2787},"2. Обмежені контексти. Інтеграція обмежених контекстів","/software-engineering/integrating-limited-contexts","09.software-engineering/02.integrating-limited-contexts",{"title":2789,"path":2790,"stem":2791},"3. Реалізація простої бізнес-логіки","/software-engineering/simple","09.software-engineering/03.simple",{"title":2793,"path":2794,"stem":2795},"4. Опрацювання складної бізнес-логіки","/software-engineering/complex-business-logic","09.software-engineering/04.complex-business-logic",{"title":2797,"path":2798,"stem":2799},"5. Моделювання фактора часу. Подієво-орієнтована архітектура.","/software-engineering/modelling-the-time-factor","09.software-engineering/05.modelling-the-time-factor",{"title":2801,"path":2802,"stem":2803},"6. Архітектурні патерни","/software-engineering/architectural-patterns","09.software-engineering/06.architectural-patterns",{"title":2805,"path":2806,"stem":2807},"Паттерни взаємодії","/software-engineering/patterns-of-interaction","09.software-engineering/07.patterns-of-interaction",{"title":2809,"path":2810,"stem":2811},"Евристика проєктування","/software-engineering/design-heuristics","09.software-engineering/08.design-heuristics",{"title":2813,"path":2814,"stem":2815},"Еволюція проєктних рішень","/software-engineering/evolution-of-design-solutions","09.software-engineering/09.evolution-of-design-solutions",{"title":2817,"path":2818,"stem":2819},"EventStorming","/software-engineering/eventstorming","09.software-engineering/10.eventstorming",{"title":2821,"path":2822,"stem":2823},"DDD на практиці","/software-engineering/ddd-in-practice","09.software-engineering/11.ddd-in-practice",{"title":2825,"icon":943,"path":2826,"stem":2827,"children":2828,"page":59},"DDD","/ddd","10.ddd",[2829,2833,2837,2841,2845,2849,2853,2857,2861,2865,2869,2873,2877],{"title":2830,"path":2831,"stem":2832},"Аналіз предметної області","/ddd/domain-analysis","10.ddd/01.domain-analysis",{"title":2834,"path":2835,"stem":2836},"Експертні знання про предметну область","/ddd/domain-expert-knowledge","10.ddd/02.domain-expert-knowledge",{"title":2838,"path":2839,"stem":2840},"Як осмислити складність предметної області","/ddd/managing-domain-complexity","10.ddd/03.managing-domain-complexity",{"title":2842,"path":2843,"stem":2844},"Інтеграція обмежених контекстів","/ddd/bounded-context-integration","10.ddd/04.bounded-context-integration",{"title":2846,"path":2847,"stem":2848},"Реалізація простої бізнес-логіки","/ddd/simple-business-logic","10.ddd/05.simple-business-logic",{"title":2850,"path":2851,"stem":2852},"Обробка складної бізнес-логіки","/ddd/complex-business-logic","10.ddd/06.complex-business-logic",{"title":2854,"path":2855,"stem":2856},"Моделювання фактора часу","/ddd/time-modeling","10.ddd/07.time-modeling",{"title":2858,"path":2859,"stem":2860},"Глава 8. Архітектурні Патерни","/ddd/architectural-patterns","10.ddd/08.architectural-patterns",{"title":2862,"path":2863,"stem":2864},"Глава 9. Патерни Взаємодії","/ddd/interaction-patterns","10.ddd/09.interaction-patterns",{"title":2866,"path":2867,"stem":2868},"Глава 10. Проектні Евристики","/ddd/design-heuristics","10.ddd/10.design-heuristics",{"title":2870,"path":2871,"stem":2872},"Глава 11. Еволюція Проектних Рішень","/ddd/evolution-of-design-decisions","10.ddd/11.evolution-of-design-decisions",{"title":2874,"path":2875,"stem":2876},"Глава 12. EventStorming","/ddd/event-storming","10.ddd/12.event-storming",{"title":2878,"path":2879,"stem":2880},"Глава 13. DDD на Практиці","/ddd/ddd-in-practice","10.ddd/13.ddd-in-practice",{"title":2882,"icon":2883,"path":2884,"stem":2885,"children":2886,"page":59},"Media Streaming","i-lucide-video","/media-streaming","11.media-streaming",[2887,2891,2895,2899,2903,2907,2911],{"title":2888,"path":2889,"stem":2890},"01. Магія Стрімінгу: Що відбувається, коли ви натискаєте \"Play\"","/media-streaming/introduction","11.media-streaming/01.introduction",{"title":2892,"path":2893,"stem":2894},"02. Анатомія Медіа: Кодеки, Контейнери та Стиснення","/media-streaming/audio-video-anatomy","11.media-streaming/02.audio-video-anatomy",{"title":2896,"path":2897,"stem":2898},"03. The Gym: FFmpeg Deep Dive","/media-streaming/ffmpeg-gym","11.media-streaming/03.ffmpeg-gym",{"title":2900,"path":2901,"stem":2902},"04. HLS Protocol: HTTP Live Streaming у Деталях","/media-streaming/hls-protocol","11.media-streaming/04.hls-protocol",{"title":2904,"path":2905,"stem":2906},"05. DASH Protocol: Відкритий Стандарт","/media-streaming/dash-protocol","11.media-streaming/05.dash-protocol",{"title":2908,"path":2909,"stem":2910},"06. Масштабування: CDN та Adaptive Bitrate","/media-streaming/cdn-and-adaptive-bitrate","11.media-streaming/06.cdn-and-adaptive-bitrate",{"title":2912,"path":2913,"stem":2914},"07. Війна із Затримкою (Latency)","/media-streaming/realtime-latency","11.media-streaming/07.realtime-latency",{"title":2916,"icon":2917,"path":2918,"stem":2919,"children":2920,"page":59},"HTML & CSS","i-devicon-html5","/html-css","12.html-css",[2921,2925,2929,2933,2937,2941,2945,2949,2953,2957,2961,2965,2969,2973,2977,2981,2985,2989,2993,2997,3001,3005,3009,3013,3017,3021,3025,3029,3033,3037],{"title":2922,"path":2923,"stem":2924},"Вступ до HTML. Структура документа","/html-css/intro-html-structure","12.html-css/01.intro-html-structure",{"title":2926,"path":2927,"stem":2928},"Форматування тексту в HTML","/html-css/html-text-formatting","12.html-css/02.html-text-formatting",{"title":2930,"path":2931,"stem":2932},"Посилання та зображення в HTML","/html-css/html-links-images","12.html-css/03.html-links-images",{"title":2934,"path":2935,"stem":2936},"Списки та таблиці в HTML","/html-css/html-lists-tables","12.html-css/04.html-lists-tables",{"title":2938,"path":2939,"stem":2940},"Форми в HTML","/html-css/html-forms","12.html-css/05.html-forms",{"title":2942,"path":2943,"stem":2944},"Семантичні елементи HTML5","/html-css/html-semantic-elements","12.html-css/06.html-semantic-elements",{"title":2946,"path":2947,"stem":2948},"Мультимедіа та розширені елементи HTML","/html-css/html-multimedia-advanced","12.html-css/07.html-multimedia-advanced",{"title":2950,"path":2951,"stem":2952},"Мікророзмітка та SEO в HTML","/html-css/html-microdata-seo","12.html-css/08.html-microdata-seo",{"title":2954,"path":2955,"stem":2956},"Вступ до CSS. Селектори та специфічність","/html-css/css-intro-selectors","12.html-css/09.css-intro-selectors",{"title":2958,"path":2959,"stem":2960},"Блокова модель CSS. Відступи. Box Sizing","/html-css/css-box-model","12.html-css/10.css-box-model",{"title":2962,"path":2963,"stem":2964},"Розміри у CSS: повний довідник одиниць і ключових слів","/html-css/10a.css-sizing","12.html-css/10a.css-sizing",{"title":2966,"path":2967,"stem":2968},"Типографіка в CSS. Шрифти та текст","/html-css/css-typography","12.html-css/11.css-typography",{"title":2970,"path":2971,"stem":2972},"Кольори та фони в CSS","/html-css/css-colors-backgrounds","12.html-css/12.css-colors-backgrounds",{"title":2974,"path":2975,"stem":2976},"Тіні та фільтри в CSS","/html-css/12b.css-shadows-filters","12.html-css/12b.css-shadows-filters",{"title":2978,"path":2979,"stem":2980},"CSS Flexbox: Фундамент гнучких макетів","/html-css/css-flexbox-fundamentals","12.html-css/13.css-flexbox-fundamentals",{"title":2982,"path":2983,"stem":2984},"CSS Flexbox: Вирівнювання та Позиціонування","/html-css/css-flexbox-alignment-sizing-and-patterns","12.html-css/14.css-flexbox-alignment-sizing-and-patterns",{"title":2986,"path":2987,"stem":2988},"CSS Grid. Двовимірний макет. Частина 1","/html-css/css-layout-grid","12.html-css/15.css-layout-grid",{"title":2990,"path":2991,"stem":2992},"CSS Grid. Двовимірний макет. Частина 2","/html-css/css-layout-grid-advanced","12.html-css/16.css-layout-grid-advanced",{"title":2994,"path":2995,"stem":2996},"Позиціонування в CSS. Z-index. Stacking Context","/html-css/css-positioning","12.html-css/17.css-positioning",{"title":2998,"path":2999,"stem":3000},"CSS Анімації та Переходи","/html-css/css-animations-transitions","12.html-css/18.css-animations-transitions",{"title":3002,"path":3003,"stem":3004},"Адаптивний дизайн. Media Queries. Частина 1","/html-css/css-responsive-media-queries","12.html-css/19.css-responsive-media-queries",{"title":3006,"path":3007,"stem":3008},"Адаптивний дизайн. Частина 2: clamp(), Container Queries, @layer","/html-css/css-responsive-advanced","12.html-css/20.css-responsive-advanced",{"title":3010,"path":3011,"stem":3012},"CSS Custom Properties. Методології. Сучасний CSS","/html-css/css-variables-methodologies","12.html-css/21.css-variables-methodologies",{"title":3014,"path":3015,"stem":3016},"Сучасний CSS 2023–2025: Нові можливості","/html-css/css-modern-features","12.html-css/22.css-modern-features",{"title":3018,"path":3019,"stem":3020},"CSS Nesting, @layer, @scope та @property: нативний препроцесор","/html-css/22a.css-nesting-modern-syntax","12.html-css/22a.css-nesting-modern-syntax",{"title":3022,"path":3023,"stem":3024},"CSS для форм та інтерактивних станів","/html-css/css-forms-interactive-states","12.html-css/23.css-forms-interactive-states",{"title":3026,"path":3027,"stem":3028},"Доступність у CSS (CSS Accessibility)","/html-css/css-accessibility","12.html-css/24.css-accessibility",{"title":3030,"path":3031,"stem":3032},"CSS-функції та сучасні sizing primitives","/html-css/css-functions-sizing","12.html-css/25.css-functions-sizing",{"title":3034,"path":3035,"stem":3036},"Rendering Pipeline і CSS Performance","/html-css/css-rendering-performance","12.html-css/26.css-rendering-performance",{"title":3038,"path":3039,"stem":3040},"CSS Best Practices: типові ситуації та правильні рішення","/html-css/css-best-practices","12.html-css/27.css-best-practices",{"title":3042,"path":3043,"stem":3044,"children":3045,"page":59},"AWS","/aws","13.aws",[3046,3050,3054],{"title":3047,"path":3048,"stem":3049},"Реєстрація AWS акаунту та студентські програми","/aws/account-registration","13.aws/00.account-registration",{"title":3051,"path":3052,"stem":3053},"Вступ до хмарних обчислень та AWS","/aws/introduction-to-cloud","13.aws/01.introduction-to-cloud",{"title":3055,"path":3056,"stem":3057},"AWS IAM — Identity and Access Management","/aws/iam","13.aws/02.iam",{"title":3059,"path":3060,"stem":3061,"children":3062,"page":59},"Tailwind","/tailwind","21.tailwind",[3063,3067,3071,3075,3079,3083,3087,3091],{"title":3064,"path":3065,"stem":3066},"Що таке Tailwind CSS і навіщо він потрібен","/tailwind/tailwind-intro-philosophy","21.tailwind/01.tailwind-intro-philosophy",{"title":3068,"path":3069,"stem":3070},"Встановлення та налаштування Tailwind CSS v4","/tailwind/tailwind-installation-setup","21.tailwind/02.tailwind-installation-setup",{"title":3072,"path":3073,"stem":3074},"Utility-класи: основи та система Tailwind","/tailwind/tailwind-utility-classes-core","21.tailwind/03.tailwind-utility-classes-core",{"title":3076,"path":3077,"stem":3078},"Layout: Flexbox та Grid через Tailwind","/tailwind/tailwind-flexbox-grid","21.tailwind/04.tailwind-flexbox-grid",{"title":3080,"path":3081,"stem":3082},"Кастомізація теми через @theme у Tailwind v4","/tailwind/tailwind-theme-customization","21.tailwind/05.tailwind-theme-customization",{"title":3084,"path":3085,"stem":3086},"Варіанти: hover, focus, responsive, dark mode та нові v4","/tailwind/tailwind-variants-states","21.tailwind/06.tailwind-variants-states",{"title":3088,"path":3089,"stem":3090},"Типографіка та система кольорів у Tailwind v4","/tailwind/tailwind-typography-colors","21.tailwind/07.tailwind-typography-colors",{"title":3092,"path":3093,"stem":3094},"Компоненти та повторюваність: @apply, @utility та патерни","/tailwind/tailwind-components-patterns","21.tailwind/08.tailwind-components-patterns",{"title":3096,"path":3097,"stem":3098},"Тестування компонентів діаграм","/test-components","98.test-components",{"id":3100,"title":2727,"body":3101,"description":17366,"extension":17367,"links":17368,"meta":17369,"navigation":3771,"path":2728,"seo":17370,"stem":2729,"__hash__":17371},"docs/07.tools/01.docker/16.docker-compose-basics.md",{"type":3102,"value":3103,"toc":17287},"minimark",[3104,3108,3113,3117,3128,3318,3324,3366,3373,3395,3404,3411,3415,3418,3422,3427,3436,3441,3487,3491,3497,3503,3602,3607,3621,3626,3640,3650,3654,3664,3669,3699,3705,3788,3800,3802,3806,3810,3813,3821,3872,3877,3891,3896,3946,3951,3992,3997,4038,4043,4062,4066,4100,4103,4107,4110,4198,4203,4219,4226,4231,4237,4267,4276,4278,4282,4285,4289,4294,4593,4598,4607,4652,4702,4710,4758,4814,4819,4842,4853,4858,4881,4890,4897,4901,4923,4927,4991,4995,5072,5077,5085,5088,5092,5099,5106,5118,5123,5129,5135,5553,5578,5586,5607,5614,5923,5939,5946,6024,6029,6132,6158,6160,6164,6167,6181,6185,7292,7298,7332,7337,7376,7380,7574,7587,7589,7593,7600,7619,7624,7693,7697,8755,8761,8791,8796,8803,8823,8827,9020,9040,9042,9046,9049,9140,9148,9150,9154,9158,9164,9168,9174,9178,9306,9313,9337,9342,9401,9408,9419,9425,9433,9477,9481,9501,9504,9511,9557,9565,9573,9590,9594,9646,9651,9656,9702,9707,9730,9769,9773,9796,9799,9804,9863,9868,9917,9922,9963,9968,9996,10001,10047,10051,10110,10121,10125,10171,10176,10324,10329,10391,10396,10490,10495,10533,10536,10540,10544,10614,10619,10696,10701,10743,10749,10753,10758,10849,10856,10883,10890,10904,10910,10918,10923,10926,11057,11062,11170,11178,11183,11190,11240,11245,11252,11256,11345,11350,11374,11376,11380,11384,11392,11471,11479,11541,11550,11625,11629,11638,11675,11684,11779,11788,11871,11880,11903,11907,11916,11992,12001,12040,12049,12088,12091,12100,12162,12168,12173,12253,12255,12259,12263,12269,12277,12467,12480,12484,12490,12495,12499,12641,12646,12717,12722,12747,12754,12758,12763,12768,12773,12842,12847,12937,12942,13066,13071,13151,13156,13176,13206,13210,13221,13226,13231,13311,13319,13325,13332,13337,13340,13381,13384,13410,13424,13426,13430,13433,13453,13460,13537,13541,13591,13595,14197,14201,14478,14482,14750,14753,14816,14820,15007,15011,15016,15114,15119,15213,15218,15398,15400,15404,15408,15784,15788,15916,15918,15922,15926,15931,15964,15969,15999,16014,16018,16021,16025,16087,16093,16097,16171,16175,16179,16201,16205,16238,16242,16248,16293,16297,16304,16310,16313,16317,16321,16350,16354,16383,16385,16389,16392,16396,16425,16430,16458,16463,16495,16500,16529,16534,16541,16543,16547,16551,16556,16561,16566,16591,16595,16628,16633,16636,16640,16657,16663,16668,16671,16682,16727,16729,16733,16738,16741,16746,16752,16756,16783,16788,16929,16938,16943,16946,16960,16964,16975,16979,17029,17034,17037,17041,17058,17062,17109,17111,17115,17120,17123,17128,17145,17149,17166,17171,17177,17182,17185,17189,17205,17210,17213,17218,17232,17236,17253,17257,17276,17278,17283],[3105,3106,2727],"h1",{"id":3107},"docker-compose-оркестрація-контейнерів",[3109,3110,3112],"h2",{"id":3111},"проблема-ручного-управління-контейнерами","Проблема ручного управління контейнерами",[3114,3115,3116],"p",{},"Уявіть, що ви розробляєте веб-застосунок з типовою архітектурою: фронтенд на React, бекенд на .NET, база даних PostgreSQL, Redis для кешування, Nginx як reverse proxy. У попередній статті ми навчилися запускати кожен компонент як окремий контейнер та з'єднувати їх через Docker networks.",[3114,3118,3119,3123,3124,3127],{},[3120,3121,3122],"strong",{},"Але виникає проблема:"," Щоб запустити весь застосунок, вам потрібно виконати ",[3120,3125,3126],{},"десятки команд"," у правильній послідовності:",[3129,3130,3132,3149,3160,3170,3173,3183,3193,3203,3206,3216,3226,3229,3239,3249,3252,3262,3272,3275,3285,3295,3298,3308],"terminal-preview",{"title":3131},"Ручний запуск контейнерів (bash)",[3133,3134,3137,3143,3144],"div",{"className":3135},[3136],"line",[3138,3139,3142],"span",{"className":3140},[3141],"opacity-40","$"," ",[3138,3145,3148],{"className":3146},[3147],"text-gray-400","# 1. Створити мережі",[3133,3150,3152,3143,3155],{"className":3151},[3136],[3138,3153,3142],{"className":3154},[3141],[3120,3156,3159],{"className":3157},[3158],"font-bold","docker network create frontend-network",[3133,3161,3163,3143,3166],{"className":3162},[3136],[3138,3164,3142],{"className":3165},[3141],[3120,3167,3169],{"className":3168},[3158],"docker network create backend-network",[3133,3171],{"className":3172},[3136],[3133,3174,3176,3143,3179],{"className":3175},[3136],[3138,3177,3142],{"className":3178},[3141],[3138,3180,3182],{"className":3181},[3147],"# 2. Створити volumes",[3133,3184,3186,3143,3189],{"className":3185},[3136],[3138,3187,3142],{"className":3188},[3141],[3120,3190,3192],{"className":3191},[3158],"docker volume create postgres-data",[3133,3194,3196,3143,3199],{"className":3195},[3136],[3138,3197,3142],{"className":3198},[3141],[3120,3200,3202],{"className":3201},[3158],"docker volume create redis-data",[3133,3204],{"className":3205},[3136],[3133,3207,3209,3143,3212],{"className":3208},[3136],[3138,3210,3142],{"className":3211},[3141],[3138,3213,3215],{"className":3214},[3147],"# 3. Запустити PostgreSQL",[3133,3217,3219,3143,3222],{"className":3218},[3136],[3138,3220,3142],{"className":3221},[3141],[3120,3223,3225],{"className":3224},[3158],"docker run -d --name db --network backend-network -e POSTGRES_PASSWORD=mysecret -e POSTGRES_DB=myapp -v postgres-data:/var/lib/postgresql/data postgres:16",[3133,3227],{"className":3228},[3136],[3133,3230,3232,3143,3235],{"className":3231},[3136],[3138,3233,3142],{"className":3234},[3141],[3138,3236,3238],{"className":3237},[3147],"# 4. Запустити Redis",[3133,3240,3242,3143,3245],{"className":3241},[3136],[3138,3243,3142],{"className":3244},[3141],[3120,3246,3248],{"className":3247},[3158],"docker run -d --name cache --network backend-network -v redis-data:/data redis:7-alpine",[3133,3250],{"className":3251},[3136],[3133,3253,3255,3143,3258],{"className":3254},[3136],[3138,3256,3142],{"className":3257},[3141],[3138,3259,3261],{"className":3260},[3147],"# 5. Запустити Backend",[3133,3263,3265,3143,3268],{"className":3264},[3136],[3138,3266,3142],{"className":3267},[3141],[3120,3269,3271],{"className":3270},[3158],"docker run -d --name backend --network frontend-network --network backend-network -e DATABASE_URL=postgres://postgres:mysecret@db:5432/myapp -e REDIS_URL=redis://cache:6379 myapp-api:latest",[3133,3273],{"className":3274},[3136],[3133,3276,3278,3143,3281],{"className":3277},[3136],[3138,3279,3142],{"className":3280},[3141],[3138,3282,3284],{"className":3283},[3147],"# 6. Запустити Frontend",[3133,3286,3288,3143,3291],{"className":3287},[3136],[3138,3289,3142],{"className":3290},[3141],[3120,3292,3294],{"className":3293},[3158],"docker run -d --name frontend --network frontend-network -p 3000:3000 myapp-frontend:latest",[3133,3296],{"className":3297},[3136],[3133,3299,3301,3143,3304],{"className":3300},[3136],[3138,3302,3142],{"className":3303},[3141],[3138,3305,3307],{"className":3306},[3147],"# 7. Запустити Nginx",[3133,3309,3311,3143,3314],{"className":3310},[3136],[3138,3312,3142],{"className":3313},[3141],[3120,3315,3317],{"className":3316},[3158],"docker run -d --name nginx --network frontend-network -p 80:80 -v ./nginx.conf:/etc/nginx/nginx.conf:ro nginx:alpine",[3114,3319,3320,3323],{},[3120,3321,3322],{},"Це лише запуск."," Тепер уявіть, що вам потрібно:",[3325,3326,3327,3338,3346,3354,3360],"ul",{},[3328,3329,3330,3333,3334],"li",{},[3120,3331,3332],{},"Зупинити весь застосунок"," — 5 команд ",[3335,3336,3337],"code",{},"docker stop",[3328,3339,3340,3333,3343],{},[3120,3341,3342],{},"Видалити контейнери",[3335,3344,3345],{},"docker rm",[3328,3347,3348,3333,3351],{},[3120,3349,3350],{},"Переглянути логи всіх сервісів",[3335,3352,3353],{},"docker logs",[3328,3355,3356,3359],{},[3120,3357,3358],{},"Оновити один сервіс"," — зупинити, видалити, пересобрати образ, запустити знову",[3328,3361,3362,3365],{},[3120,3363,3364],{},"Передати колезі"," — надіслати 50 рядків bash-команд і сподіватися, що він не зробить помилку",[3114,3367,3368,3369,3372],{},"Це ",[3120,3370,3371],{},"неефективно, схильне до помилок та нескалабельно",". Ви забудете створити volume, підключите контейнер не до тієї мережі, або запустите сервіси у неправильному порядку (backend до того, як база даних готова).",[3114,3374,3375,3378,3379,3382,3383,3386,3387,3390,3391,3394],{},[3120,3376,3377],{},"Рішення:"," Docker Compose — інструмент, що дозволяє описати всю архітектуру застосунку у ",[3120,3380,3381],{},"одному YAML-файлі"," та керувати нею ",[3120,3384,3385],{},"однією командою",". Замість 50 рядків bash-скриптів — 50 рядків декларативної конфігурації. Замість ",[3335,3388,3389],{},"docker run && docker run && ..."," — просто ",[3335,3392,3393],{},"docker compose up",".",[3114,3396,3397],{},[3398,3399],"img",{"alt":3400,"className":3401,"src":3403},"Порівняння ручного запуску багатоконтейнерного застосунку через багато docker run команд і запуску через один docker compose up",[3402],"diagram-img","/images/tools/docker/docker-compose-basics/01.png",[3114,3405,3406,3407,3410],{},"У цій статті ми детально розглянемо Docker Compose: від базового синтаксису ",[3335,3408,3409],{},"docker-compose.yml"," до просунутих сценаріїв з profiles, extends, та multi-stage deployments. Ви навчитеся організовувати development та production environments, керувати залежностями між сервісами, та автоматизувати lifecycle застосунку.",[3412,3413,3414],"note",{},"Ця стаття передбачає розуміння Docker basics (контейнери, образи, volumes, networks) з попередніх статей. Тут ми зосередимося на оркестрації через Compose.",[3416,3417],"hr",{},[3109,3419,3421],{"id":3420},"що-таке-docker-compose","Що таке Docker Compose",[3423,3424,3426],"h3",{"id":3425},"визначення-та-призначення","Визначення та призначення",[3114,3428,3429,3432,3433,3435],{},[3120,3430,3431],{},"Docker Compose"," — це інструмент для визначення та запуску multi-container Docker-застосунків. Ви описуєте архітектуру застосунку у файлі ",[3335,3434,3409],{}," (YAML-формат), а Compose автоматично створює мережі, volumes, запускає контейнери у правильному порядку та керує їхнім життєвим циклом.",[3114,3437,3438],{},[3120,3439,3440],{},"Ключові концепції:",[3442,3443,3444,3458,3467,3479],"ol",{},[3328,3445,3446,3449,3450,3453,3454,3457],{},[3120,3447,3448],{},"Декларативність"," — ви описуєте ",[3120,3451,3452],{},"що"," має бути (desired state), а не ",[3120,3455,3456],{},"як"," це зробити (imperative commands)",[3328,3459,3460,3463,3464,3466],{},[3120,3461,3462],{},"Ідемпотентність"," — ",[3335,3465,3393],{}," можна запускати кілька разів — Compose створить лише те, чого немає",[3328,3468,3469,3463,3472,3474,3475,3478],{},[3120,3470,3471],{},"Одна команда",[3335,3473,3393],{}," для запуску, ",[3335,3476,3477],{},"docker compose down"," для зупинки та очищення",[3328,3480,3481,3463,3484,3486],{},[3120,3482,3483],{},"Портабельність",[3335,3485,3409],{}," можна передати колезі, і він отримає ідентичне середовище",[3423,3488,3490],{"id":3489},"compose-vs-kubernetes","Compose vs Kubernetes",[3114,3492,3493,3496],{},[3120,3494,3495],{},"Питання:"," Чи не є Docker Compose \"іграшковою\" версією Kubernetes?",[3114,3498,3499,3502],{},[3120,3500,3501],{},"Відповідь:"," Ні. Compose та Kubernetes вирішують різні завдання:",[3504,3505,3506,3520],"table",{},[3507,3508,3509],"thead",{},[3510,3511,3512,3516,3518],"tr",{},[3513,3514,3515],"th",{},"Аспект",[3513,3517,3431],{},[3513,3519,2735],{},[3521,3522,3523,3537,3550,3563,3576,3589],"tbody",{},[3510,3524,3525,3531,3534],{},[3526,3527,3528],"td",{},[3120,3529,3530],{},"Призначення",[3526,3532,3533],{},"Локальна розробка, single-host deployment",[3526,3535,3536],{},"Production orchestration, multi-host clusters",[3510,3538,3539,3544,3547],{},[3526,3540,3541],{},[3120,3542,3543],{},"Складність",[3526,3545,3546],{},"Простий YAML, 50-100 рядків",[3526,3548,3549],{},"Складні manifests, 500+ рядків",[3510,3551,3552,3557,3560],{},[3526,3553,3554],{},[3120,3555,3556],{},"Масштабування",[3526,3558,3559],{},"Обмежене (один хост)",[3526,3561,3562],{},"Автоматичне (кілька хостів, auto-scaling)",[3510,3564,3565,3570,3573],{},[3526,3566,3567],{},[3120,3568,3569],{},"High Availability",[3526,3571,3572],{},"Немає (якщо хост падає — все падає)",[3526,3574,3575],{},"Так (self-healing, replication)",[3510,3577,3578,3583,3586],{},[3526,3579,3580],{},[3120,3581,3582],{},"Навчання",[3526,3584,3585],{},"1-2 дні",[3526,3587,3588],{},"2-4 тижні",[3510,3590,3591,3596,3599],{},[3526,3592,3593],{},[3120,3594,3595],{},"Use Case",[3526,3597,3598],{},"Development, testing, small production",[3526,3600,3601],{},"Large-scale production, microservices",[3114,3603,3604],{},[3120,3605,3606],{},"Коли використовувати Compose:",[3325,3608,3609,3612,3615,3618],{},[3328,3610,3611],{},"Локальна розробка (замість ручного запуску контейнерів)",[3328,3613,3614],{},"CI/CD pipelines (integration tests з реальними сервісами)",[3328,3616,3617],{},"Малі production deployments (1-2 сервери, до 10-20 контейнерів)",[3328,3619,3620],{},"Прототипування перед переходом на Kubernetes",[3114,3622,3623],{},[3120,3624,3625],{},"Коли використовувати Kubernetes:",[3325,3627,3628,3631,3634,3637],{},[3328,3629,3630],{},"Production з високими вимогами до availability (99.9%+)",[3328,3632,3633],{},"Масштабування на десятки/сотні серверів",[3328,3635,3636],{},"Складні microservices-архітектури (50+ сервісів)",[3328,3638,3639],{},"Потреба в auto-scaling, rolling updates, service mesh",[3641,3642,3643,3646,3647,3649],"tip",{},[3120,3644,3645],{},"Best Practice:"," Використовуйте Compose для розробки, навіть якщо production буде на Kubernetes. Compose простіший для локального тестування, а перехід на Kubernetes можна зробити пізніше через інструменти типу Kompose (конвертує ",[3335,3648,3409],{}," у Kubernetes manifests).",[3423,3651,3653],{"id":3652},"встановлення-docker-compose","Встановлення Docker Compose",[3114,3655,3656,3659,3660,3663],{},[3120,3657,3658],{},"Docker Compose V2"," (сучасна версія) вбудований у Docker Desktop та Docker Engine 20.10+. Команда: ",[3335,3661,3662],{},"docker compose"," (без дефісу).",[3114,3665,3666],{},[3120,3667,3668],{},"Перевірити версію:",[3670,3671,3676],"pre",{"className":3672,"code":3673,"language":3674,"meta":3675,"style":3675},"language-bash shiki shiki-themes light-plus dark-plus dark-plus","docker compose version\n# Вивід: Docker Compose version v2.24.5\n","bash","",[3335,3677,3678,3692],{"__ignoreMap":3675},[3138,3679,3681,3685,3689],{"class":3136,"line":3680},1,[3138,3682,3684],{"class":3683},"s8Opu","docker",[3138,3686,3688],{"class":3687},"sbdoH"," compose",[3138,3690,3691],{"class":3687}," version\n",[3138,3693,3695],{"class":3136,"line":3694},2,[3138,3696,3698],{"class":3697},"spJ8K","# Вивід: Docker Compose version v2.24.5\n",[3114,3700,3701,3704],{},[3120,3702,3703],{},"Якщо Compose не встановлено"," (старі версії Docker):",[3670,3706,3708],{"className":3672,"code":3707,"language":3674,"meta":3675,"style":3675},"# Linux\nsudo curl -L \"https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)\" -o /usr/local/bin/docker-compose\nsudo chmod +x /usr/local/bin/docker-compose\n\n# Перевірка\ndocker-compose --version\n",[3335,3709,3710,3715,3753,3766,3773,3779],{"__ignoreMap":3675},[3138,3711,3712],{"class":3136,"line":3680},[3138,3713,3714],{"class":3697},"# Linux\n",[3138,3716,3717,3720,3723,3727,3730,3733,3736,3739,3741,3744,3747,3750],{"class":3136,"line":3694},[3138,3718,3719],{"class":3683},"sudo",[3138,3721,3722],{"class":3687}," curl",[3138,3724,3726],{"class":3725},"su1O8"," -L",[3138,3728,3729],{"class":3687}," \"https://github.com/docker/compose/releases/latest/download/docker-compose-$(",[3138,3731,3732],{"class":3683},"uname",[3138,3734,3735],{"class":3725}," -s",[3138,3737,3738],{"class":3687},")-$(",[3138,3740,3732],{"class":3683},[3138,3742,3743],{"class":3725}," -m",[3138,3745,3746],{"class":3687},")\"",[3138,3748,3749],{"class":3725}," -o",[3138,3751,3752],{"class":3687}," /usr/local/bin/docker-compose\n",[3138,3754,3756,3758,3761,3764],{"class":3136,"line":3755},3,[3138,3757,3719],{"class":3683},[3138,3759,3760],{"class":3687}," chmod",[3138,3762,3763],{"class":3687}," +x",[3138,3765,3752],{"class":3687},[3138,3767,3769],{"class":3136,"line":3768},4,[3138,3770,3772],{"emptyLinePlaceholder":3771},true,"\n",[3138,3774,3776],{"class":3136,"line":3775},5,[3138,3777,3778],{"class":3697},"# Перевірка\n",[3138,3780,3782,3785],{"class":3136,"line":3781},6,[3138,3783,3784],{"class":3683},"docker-compose",[3138,3786,3787],{"class":3725}," --version\n",[3412,3789,3790,3793,3794,3796,3797,3799],{},[3120,3791,3792],{},"Compose V1 vs V2:"," Стара версія використовувала команду ",[3335,3795,3784],{}," (з дефісом) та була окремим Python-додатком. Нова версія (",[3335,3798,3662],{}," без дефісу) написана на Go, інтегрована у Docker CLI та швидша. У цій статті ми використовуємо V2.",[3416,3801],{},[3109,3803,3805],{"id":3804},"перший-docker-composeyml","Перший docker-compose.yml",[3423,3807,3809],{"id":3808},"мінімальний-приклад","Мінімальний приклад",[3114,3811,3812],{},"Почнемо з найпростішого застосунку: один контейнер з Nginx.",[3114,3814,3815],{},[3120,3816,3817,3818,3820],{},"Створити файл ",[3335,3819,3409],{},":",[3670,3822,3826],{"className":3823,"code":3824,"language":3825,"meta":3675,"style":3675},"language-yaml shiki shiki-themes light-plus dark-plus dark-plus","services:\n  web:\n    image: nginx:alpine\n    ports:\n      - \"8080:80\"\n","yaml",[3335,3827,3828,3838,3845,3857,3864],{"__ignoreMap":3675},[3138,3829,3830,3834],{"class":3136,"line":3680},[3138,3831,3833],{"class":3832},"sKtos","services",[3138,3835,3837],{"class":3836},"sHH4Y",":\n",[3138,3839,3840,3843],{"class":3136,"line":3694},[3138,3841,3842],{"class":3832},"  web",[3138,3844,3837],{"class":3836},[3138,3846,3847,3850,3853],{"class":3136,"line":3755},[3138,3848,3849],{"class":3832},"    image",[3138,3851,3852],{"class":3836},": ",[3138,3854,3856],{"class":3855},"su9tN","nginx:alpine\n",[3138,3858,3859,3862],{"class":3136,"line":3768},[3138,3860,3861],{"class":3832},"    ports",[3138,3863,3837],{"class":3836},[3138,3865,3866,3869],{"class":3136,"line":3775},[3138,3867,3868],{"class":3836},"      - ",[3138,3870,3871],{"class":3687},"\"8080:80\"\n",[3114,3873,3874],{},[3120,3875,3876],{},"Запустити:",[3670,3878,3880],{"className":3672,"code":3879,"language":3674,"meta":3675,"style":3675},"docker compose up\n",[3335,3881,3882],{"__ignoreMap":3675},[3138,3883,3884,3886,3888],{"class":3136,"line":3680},[3138,3885,3684],{"class":3683},[3138,3887,3688],{"class":3687},[3138,3889,3890],{"class":3687}," up\n",[3114,3892,3893],{},[3120,3894,3895],{},"Вивід:",[3129,3897,3898,3907,3910,3918,3927,3935,3938],{"title":3393},[3133,3899,3901,3143,3904],{"className":3900},[3136],[3138,3902,3142],{"className":3903},[3141],[3120,3905,3393],{"className":3906},[3158],[3133,3908],{"className":3909},[3136],[3133,3911,3913],{"className":3912},[3136],[3138,3914,3917],{"className":3915},[3916,3158],"text-blue-400","[+] Running 2/2",[3133,3919,3921,3922],{"className":3920},[3136]," ✔ Network myapp_default    ",[3138,3923,3926],{"className":3924},[3925,3158],"text-green-400","Created",[3133,3928,3930,3931],{"className":3929},[3136]," ✔ Container myapp-web-1    ",[3138,3932,3934],{"className":3933},[3925,3158],"Started",[3133,3936],{"className":3937},[3136],[3133,3939,3941,3945],{"className":3940},[3136],[3138,3942,3944],{"className":3943},[3147],"web-1  |"," /docker-entrypoint.sh: Configuration complete; ready for start up",[3114,3947,3948],{},[3120,3949,3950],{},"Що відбулося:",[3442,3952,3953,3968,3978,3989],{},[3328,3954,3955,3956,3959,3960,3963,3964,3967],{},"Compose створив ",[3120,3957,3958],{},"default network"," з назвою ",[3335,3961,3962],{},"myapp_default"," (де ",[3335,3965,3966],{},"myapp"," — назва директорії)",[3328,3969,3970,3971,3963,3974,3977],{},"Compose запустив контейнер з іменем ",[3335,3972,3973],{},"myapp-web-1",[3335,3975,3976],{},"web"," — назва сервісу з YAML)",[3328,3979,3980,3981,3984,3985,3988],{},"Порт ",[3335,3982,3983],{},"80"," контейнера пробросено на ",[3335,3986,3987],{},"8080"," хоста",[3328,3990,3991],{},"Логи контейнера виводяться у термінал",[3114,3993,3994],{},[3120,3995,3996],{},"Перевірка:",[3670,3998,4000],{"className":3672,"code":3999,"language":3674,"meta":3675,"style":3675},"# У новому терміналі\ncurl http://localhost:8080\n# Вивід: \u003C!DOCTYPE html>\u003Chtml>...\n\n# Переглянути запущені контейнери\ndocker compose ps\n",[3335,4001,4002,4007,4015,4020,4024,4029],{"__ignoreMap":3675},[3138,4003,4004],{"class":3136,"line":3680},[3138,4005,4006],{"class":3697},"# У новому терміналі\n",[3138,4008,4009,4012],{"class":3136,"line":3694},[3138,4010,4011],{"class":3683},"curl",[3138,4013,4014],{"class":3687}," http://localhost:8080\n",[3138,4016,4017],{"class":3136,"line":3755},[3138,4018,4019],{"class":3697},"# Вивід: \u003C!DOCTYPE html>\u003Chtml>...\n",[3138,4021,4022],{"class":3136,"line":3768},[3138,4023,3772],{"emptyLinePlaceholder":3771},[3138,4025,4026],{"class":3136,"line":3775},[3138,4027,4028],{"class":3697},"# Переглянути запущені контейнери\n",[3138,4030,4031,4033,4035],{"class":3136,"line":3781},[3138,4032,3684],{"class":3683},[3138,4034,3688],{"class":3687},[3138,4036,4037],{"class":3687}," ps\n",[3114,4039,4040],{},[3120,4041,4042],{},"Зупинити (Ctrl+C у терміналі з логами):",[3670,4044,4046],{"className":3672,"code":4045,"language":3674,"meta":3675,"style":3675},"# Або у новому терміналі\ndocker compose down\n",[3335,4047,4048,4053],{"__ignoreMap":3675},[3138,4049,4050],{"class":3136,"line":3680},[3138,4051,4052],{"class":3697},"# Або у новому терміналі\n",[3138,4054,4055,4057,4059],{"class":3136,"line":3694},[3138,4056,3684],{"class":3683},[3138,4058,3688],{"class":3687},[3138,4060,4061],{"class":3687}," down\n",[3114,4063,4064],{},[3120,4065,3895],{},[3129,4067,4068,4077,4080,4086,4094],{"title":3477},[3133,4069,4071,3143,4074],{"className":4070},[3136],[3138,4072,3142],{"className":4073},[3141],[3120,4075,3477],{"className":4076},[3158],[3133,4078],{"className":4079},[3136],[3133,4081,4083],{"className":4082},[3136],[3138,4084,3917],{"className":4085},[3916,3158],[3133,4087,3930,4089],{"className":4088},[3136],[3138,4090,4093],{"className":4091},[4092,3158],"text-rose-400","Removed",[3133,4095,3921,4097],{"className":4096},[3136],[3138,4098,4093],{"className":4099},[4092,3158],[3114,4101,4102],{},"Compose видалив контейнер та мережу. Все чисто.",[3423,4104,4106],{"id":4105},"анатомія-docker-composeyml","Анатомія docker-compose.yml",[3114,4108,4109],{},"Розберемо структуру файлу:",[3670,4111,4114],{"className":4112,"code":4113,"language":3825,"meta":3675,"style":3675},"language-yaml shiki shiki-themes light-plus dark-plus dark-plus has-diff","version: '3.8'  # [!code --]\n# Версія синтаксису Compose була обов'язковою у V1, але у V2 ігнорується.\n# Ви можете безпечно видаляти цей рядок з усіх ваших файлів.\n\nservices:       # Список сервісів (контейнерів)\n  web:          # Назва сервісу (довільна, використовується як DNS-ім'я)\n    image: nginx:alpine  # Образ для контейнера\n    ports:      # Проброс портів\n      - \"8080:80\"  # HOST:CONTAINER\n",[3335,4115,4116,4129,4134,4139,4143,4153,4163,4176,4187],{"__ignoreMap":3675},[3138,4117,4121,4124,4126],{"class":4118,"line":3680},[3136,4119,4120],"diff","remove",[3138,4122,4123],{"class":3832},"version",[3138,4125,3852],{"class":3836},[3138,4127,4128],{"class":3855},"'3.8'",[3138,4130,4131],{"class":3136,"line":3694},[3138,4132,4133],{"class":3697},"# Версія синтаксису Compose була обов'язковою у V1, але у V2 ігнорується.\n",[3138,4135,4136],{"class":3136,"line":3755},[3138,4137,4138],{"class":3697},"# Ви можете безпечно видаляти цей рядок з усіх ваших файлів.\n",[3138,4140,4141],{"class":3136,"line":3768},[3138,4142,3772],{"emptyLinePlaceholder":3771},[3138,4144,4145,4147,4150],{"class":3136,"line":3775},[3138,4146,3833],{"class":3832},[3138,4148,4149],{"class":3836},":       ",[3138,4151,4152],{"class":3697},"# Список сервісів (контейнерів)\n",[3138,4154,4155,4157,4160],{"class":3136,"line":3781},[3138,4156,3842],{"class":3832},[3138,4158,4159],{"class":3836},":          ",[3138,4161,4162],{"class":3697},"# Назва сервісу (довільна, використовується як DNS-ім'я)\n",[3138,4164,4166,4168,4170,4173],{"class":3136,"line":4165},7,[3138,4167,3849],{"class":3832},[3138,4169,3852],{"class":3836},[3138,4171,4172],{"class":3855},"nginx:alpine",[3138,4174,4175],{"class":3697},"  # Образ для контейнера\n",[3138,4177,4179,4181,4184],{"class":3136,"line":4178},8,[3138,4180,3861],{"class":3832},[3138,4182,4183],{"class":3836},":      ",[3138,4185,4186],{"class":3697},"# Проброс портів\n",[3138,4188,4190,4192,4195],{"class":3136,"line":4189},9,[3138,4191,3868],{"class":3836},[3138,4193,4194],{"class":3687},"\"8080:80\"",[3138,4196,4197],{"class":3697},"  # HOST:CONTAINER\n",[3114,4199,4200],{},[3120,4201,4202],{},"Ключові секції верхнього рівня:",[4204,4205,4206,4211,4215],"field-group",{},[4207,4208,4210],"field",{"name":3833,"type":4209},"object","Список сервісів (контейнерів). Кожен сервіс — це один або кілька контейнерів з однаковою конфігурацією. Це єдина обов'язкова секція у файлі.",[4207,4212,4214],{"name":4213,"type":4209},"networks","Визначення кастомних мереж. Якщо не вказано, Compose автоматично створює default bridge network та підключає туди всі сервіси.",[4207,4216,4218],{"name":4217,"type":4209},"volumes","Визначення named volumes для persistent storage. Зручно для баз даних, щоб дані не зникали при перезапуску контейнерів.",[3114,4220,4221],{},[3398,4222],{"alt":4223,"className":4224,"src":4225},"Анатомія docker-compose.yml: services, networks, volumes, ports, environment, depends_on і автоматичний DNS між сервісами",[3402],"/images/tools/docker/docker-compose-basics/02.png",[3114,4227,4228],{},[3120,4229,4230],{},"Назви контейнерів:",[3114,4232,4233,4234,3394],{},"Compose автоматично генерує імена контейнерів за шаблоном: ",[3335,4235,4236],{},"\u003Cproject>-\u003Cservice>-\u003Creplica>",[3325,4238,4239,4251,4259],{},[3328,4240,4241,4246,4247,4250],{},[3120,4242,4243],{},[3335,4244,4245],{},"\u003Cproject>"," — назва директорії (або вказана через ",[3335,4248,4249],{},"-p",")",[3328,4252,4253,4258],{},[3120,4254,4255],{},[3335,4256,4257],{},"\u003Cservice>"," — назва сервісу з YAML",[3328,4260,4261,4266],{},[3120,4262,4263],{},[3335,4264,4265],{},"\u003Creplica>"," — номер репліки (1, 2, 3...) для масштабування",[3114,4268,4269,4270,4272,4273,3394],{},"Приклад: ",[3335,4271,3973],{},", ",[3335,4274,4275],{},"myapp-db-1",[3416,4277],{},[3109,4279,4281],{"id":4280},"multi-container-застосунок","Multi-container застосунок",[3114,4283,4284],{},"Тепер створимо реальний застосунок з кількома сервісами: веб-сервер та база даних.",[3423,4286,4288],{"id":4287},"приклад-wordpress-mysql","Приклад: WordPress + MySQL",[3114,4290,4291],{},[3120,4292,4293],{},"docker-compose.yml:",[3670,4295,4297],{"className":3823,"code":4296,"language":3825,"meta":3675,"style":3675},"services:\n  db:\n    image: mysql:8.0\n    environment:\n      MYSQL_ROOT_PASSWORD: rootpassword\n      MYSQL_DATABASE: wordpress\n      MYSQL_USER: wpuser\n      MYSQL_PASSWORD: wppassword\n    volumes:\n      - db-data:/var/lib/mysql\n    networks:\n      - backend\n\n  wordpress:\n    image: wordpress:latest\n    ports:\n      - \"8080:80\"\n    environment:\n      WORDPRESS_DB_HOST: db:3306\n      WORDPRESS_DB_USER: wpuser\n      WORDPRESS_DB_PASSWORD: wppassword\n      WORDPRESS_DB_NAME: wordpress\n    volumes:\n      - wp-data:/var/www/html\n    networks:\n      - backend\n      - frontend\n    depends_on:\n      - db\n\nvolumes:\n  db-data:\n  wp-data:\n\nnetworks:\n  frontend:\n  backend:\n",[3335,4298,4299,4305,4312,4321,4328,4338,4348,4358,4368,4375,4383,4391,4399,4404,4412,4422,4429,4436,4443,4454,4464,4474,4484,4491,4499,4506,4513,4521,4529,4537,4542,4549,4557,4565,4570,4577,4585],{"__ignoreMap":3675},[3138,4300,4301,4303],{"class":3136,"line":3680},[3138,4302,3833],{"class":3832},[3138,4304,3837],{"class":3836},[3138,4306,4307,4310],{"class":3136,"line":3694},[3138,4308,4309],{"class":3832},"  db",[3138,4311,3837],{"class":3836},[3138,4313,4314,4316,4318],{"class":3136,"line":3755},[3138,4315,3849],{"class":3832},[3138,4317,3852],{"class":3836},[3138,4319,4320],{"class":3855},"mysql:8.0\n",[3138,4322,4323,4326],{"class":3136,"line":3768},[3138,4324,4325],{"class":3832},"    environment",[3138,4327,3837],{"class":3836},[3138,4329,4330,4333,4335],{"class":3136,"line":3775},[3138,4331,4332],{"class":3832},"      MYSQL_ROOT_PASSWORD",[3138,4334,3852],{"class":3836},[3138,4336,4337],{"class":3855},"rootpassword\n",[3138,4339,4340,4343,4345],{"class":3136,"line":3781},[3138,4341,4342],{"class":3832},"      MYSQL_DATABASE",[3138,4344,3852],{"class":3836},[3138,4346,4347],{"class":3855},"wordpress\n",[3138,4349,4350,4353,4355],{"class":3136,"line":4165},[3138,4351,4352],{"class":3832},"      MYSQL_USER",[3138,4354,3852],{"class":3836},[3138,4356,4357],{"class":3855},"wpuser\n",[3138,4359,4360,4363,4365],{"class":3136,"line":4178},[3138,4361,4362],{"class":3832},"      MYSQL_PASSWORD",[3138,4364,3852],{"class":3836},[3138,4366,4367],{"class":3855},"wppassword\n",[3138,4369,4370,4373],{"class":3136,"line":4189},[3138,4371,4372],{"class":3832},"    volumes",[3138,4374,3837],{"class":3836},[3138,4376,4378,4380],{"class":3136,"line":4377},10,[3138,4379,3868],{"class":3836},[3138,4381,4382],{"class":3855},"db-data:/var/lib/mysql\n",[3138,4384,4386,4389],{"class":3136,"line":4385},11,[3138,4387,4388],{"class":3832},"    networks",[3138,4390,3837],{"class":3836},[3138,4392,4394,4396],{"class":3136,"line":4393},12,[3138,4395,3868],{"class":3836},[3138,4397,4398],{"class":3855},"backend\n",[3138,4400,4402],{"class":3136,"line":4401},13,[3138,4403,3772],{"emptyLinePlaceholder":3771},[3138,4405,4407,4410],{"class":3136,"line":4406},14,[3138,4408,4409],{"class":3832},"  wordpress",[3138,4411,3837],{"class":3836},[3138,4413,4415,4417,4419],{"class":3136,"line":4414},15,[3138,4416,3849],{"class":3832},[3138,4418,3852],{"class":3836},[3138,4420,4421],{"class":3855},"wordpress:latest\n",[3138,4423,4425,4427],{"class":3136,"line":4424},16,[3138,4426,3861],{"class":3832},[3138,4428,3837],{"class":3836},[3138,4430,4432,4434],{"class":3136,"line":4431},17,[3138,4433,3868],{"class":3836},[3138,4435,3871],{"class":3687},[3138,4437,4439,4441],{"class":3136,"line":4438},18,[3138,4440,4325],{"class":3832},[3138,4442,3837],{"class":3836},[3138,4444,4446,4449,4451],{"class":3136,"line":4445},19,[3138,4447,4448],{"class":3832},"      WORDPRESS_DB_HOST",[3138,4450,3852],{"class":3836},[3138,4452,4453],{"class":3855},"db:3306\n",[3138,4455,4457,4460,4462],{"class":3136,"line":4456},20,[3138,4458,4459],{"class":3832},"      WORDPRESS_DB_USER",[3138,4461,3852],{"class":3836},[3138,4463,4357],{"class":3855},[3138,4465,4467,4470,4472],{"class":3136,"line":4466},21,[3138,4468,4469],{"class":3832},"      WORDPRESS_DB_PASSWORD",[3138,4471,3852],{"class":3836},[3138,4473,4367],{"class":3855},[3138,4475,4477,4480,4482],{"class":3136,"line":4476},22,[3138,4478,4479],{"class":3832},"      WORDPRESS_DB_NAME",[3138,4481,3852],{"class":3836},[3138,4483,4347],{"class":3855},[3138,4485,4487,4489],{"class":3136,"line":4486},23,[3138,4488,4372],{"class":3832},[3138,4490,3837],{"class":3836},[3138,4492,4494,4496],{"class":3136,"line":4493},24,[3138,4495,3868],{"class":3836},[3138,4497,4498],{"class":3855},"wp-data:/var/www/html\n",[3138,4500,4502,4504],{"class":3136,"line":4501},25,[3138,4503,4388],{"class":3832},[3138,4505,3837],{"class":3836},[3138,4507,4509,4511],{"class":3136,"line":4508},26,[3138,4510,3868],{"class":3836},[3138,4512,4398],{"class":3855},[3138,4514,4516,4518],{"class":3136,"line":4515},27,[3138,4517,3868],{"class":3836},[3138,4519,4520],{"class":3855},"frontend\n",[3138,4522,4524,4527],{"class":3136,"line":4523},28,[3138,4525,4526],{"class":3832},"    depends_on",[3138,4528,3837],{"class":3836},[3138,4530,4532,4534],{"class":3136,"line":4531},29,[3138,4533,3868],{"class":3836},[3138,4535,4536],{"class":3855},"db\n",[3138,4538,4540],{"class":3136,"line":4539},30,[3138,4541,3772],{"emptyLinePlaceholder":3771},[3138,4543,4545,4547],{"class":3136,"line":4544},31,[3138,4546,4217],{"class":3832},[3138,4548,3837],{"class":3836},[3138,4550,4552,4555],{"class":3136,"line":4551},32,[3138,4553,4554],{"class":3832},"  db-data",[3138,4556,3837],{"class":3836},[3138,4558,4560,4563],{"class":3136,"line":4559},33,[3138,4561,4562],{"class":3832},"  wp-data",[3138,4564,3837],{"class":3836},[3138,4566,4568],{"class":3136,"line":4567},34,[3138,4569,3772],{"emptyLinePlaceholder":3771},[3138,4571,4573,4575],{"class":3136,"line":4572},35,[3138,4574,4213],{"class":3832},[3138,4576,3837],{"class":3836},[3138,4578,4580,4583],{"class":3136,"line":4579},36,[3138,4581,4582],{"class":3832},"  frontend",[3138,4584,3837],{"class":3836},[3138,4586,4588,4591],{"class":3136,"line":4587},37,[3138,4589,4590],{"class":3832},"  backend",[3138,4592,3837],{"class":3836},[3114,4594,4595],{},[3120,4596,4597],{},"Пояснення конфігурації:",[3114,4599,4600],{},[3120,4601,4602,4603,4606],{},"1. Сервіс ",[3335,4604,4605],{},"db"," (MySQL):",[3670,4608,4610],{"className":3823,"code":4609,"language":3825,"meta":3675,"style":3675},"db:\n  image: mysql:8.0\n  environment:\n    MYSQL_ROOT_PASSWORD: rootpassword\n    MYSQL_DATABASE: wordpress\n",[3335,4611,4612,4618,4627,4634,4643],{"__ignoreMap":3675},[3138,4613,4614,4616],{"class":3136,"line":3680},[3138,4615,4605],{"class":3832},[3138,4617,3837],{"class":3836},[3138,4619,4620,4623,4625],{"class":3136,"line":3694},[3138,4621,4622],{"class":3832},"  image",[3138,4624,3852],{"class":3836},[3138,4626,4320],{"class":3855},[3138,4628,4629,4632],{"class":3136,"line":3755},[3138,4630,4631],{"class":3832},"  environment",[3138,4633,3837],{"class":3836},[3138,4635,4636,4639,4641],{"class":3136,"line":3768},[3138,4637,4638],{"class":3832},"    MYSQL_ROOT_PASSWORD",[3138,4640,3852],{"class":3836},[3138,4642,4337],{"class":3855},[3138,4644,4645,4648,4650],{"class":3136,"line":3775},[3138,4646,4647],{"class":3832},"    MYSQL_DATABASE",[3138,4649,3852],{"class":3836},[3138,4651,4347],{"class":3855},[3325,4653,4654,4662,4677,4692],{},[3328,4655,4656,4661],{},[3120,4657,4658],{},[3335,4659,4660],{},"image"," — використовувати офіційний образ MySQL версії 8.0",[3328,4663,4664,4669,4670,4673,4674,4250],{},[3120,4665,4666],{},[3335,4667,4668],{},"environment"," — змінні середовища для конфігурації MySQL (аналог ",[3335,4671,4672],{},"-e"," у ",[3335,4675,4676],{},"docker run",[3328,4678,4679,4683,4684,4687,4688,4691],{},[3120,4680,4681],{},[3335,4682,4217],{}," — монтувати named volume ",[3335,4685,4686],{},"db-data"," до ",[3335,4689,4690],{},"/var/lib/mysql"," (де MySQL зберігає дані)",[3328,4693,4694,4698,4699],{},[3120,4695,4696],{},[3335,4697,4213],{}," — підключити до мережі ",[3335,4700,4701],{},"backend",[3114,4703,4704],{},[3120,4705,4706,4707,3820],{},"2. Сервіс ",[3335,4708,4709],{},"wordpress",[3670,4711,4713],{"className":3823,"code":4712,"language":3825,"meta":3675,"style":3675},"wordpress:\n  image: wordpress:latest\n  ports:\n    - \"8080:80\"\n  environment:\n    WORDPRESS_DB_HOST: db:3306\n",[3335,4714,4715,4721,4729,4736,4743,4749],{"__ignoreMap":3675},[3138,4716,4717,4719],{"class":3136,"line":3680},[3138,4718,4709],{"class":3832},[3138,4720,3837],{"class":3836},[3138,4722,4723,4725,4727],{"class":3136,"line":3694},[3138,4724,4622],{"class":3832},[3138,4726,3852],{"class":3836},[3138,4728,4421],{"class":3855},[3138,4730,4731,4734],{"class":3136,"line":3755},[3138,4732,4733],{"class":3832},"  ports",[3138,4735,3837],{"class":3836},[3138,4737,4738,4741],{"class":3136,"line":3768},[3138,4739,4740],{"class":3836},"    - ",[3138,4742,3871],{"class":3687},[3138,4744,4745,4747],{"class":3136,"line":3775},[3138,4746,4631],{"class":3832},[3138,4748,3837],{"class":3836},[3138,4750,4751,4754,4756],{"class":3136,"line":3781},[3138,4752,4753],{"class":3832},"    WORDPRESS_DB_HOST",[3138,4755,3852],{"class":3836},[3138,4757,4453],{"class":3855},[3325,4759,4760,4773,4784,4797],{},[3328,4761,4762,4767,4768,4770,4771,3988],{},[3120,4763,4764],{},[3335,4765,4766],{},"ports"," — пробросити порт ",[3335,4769,3983],{}," контейнера на ",[3335,4772,3987],{},[3328,4774,4775,4780,4781,4783],{},[3120,4776,4777],{},[3335,4778,4779],{},"WORDPRESS_DB_HOST: db:3306"," — WordPress підключається до MySQL за іменем сервісу ",[3335,4782,4605],{}," (Docker DNS резолвить це в IP)",[3328,4785,4786,4791,4792,4794,4795],{},[3120,4787,4788],{},[3335,4789,4790],{},"depends_on"," — запустити ",[3335,4793,4709],{}," лише після запуску ",[3335,4796,4605],{},[3328,4798,4799,4803,4804,4806,4807,4809,4810,4813],{},[3120,4800,4801],{},[3335,4802,4213],{}," — підключити до ",[3335,4805,4701],{}," (для доступу до ",[3335,4808,4605],{},") та ",[3335,4811,4812],{},"frontend"," (для ізоляції)",[3114,4815,4816],{},[3120,4817,4818],{},"3. Volumes:",[3670,4820,4822],{"className":3823,"code":4821,"language":3825,"meta":3675,"style":3675},"volumes:\n  db-data:\n  wp-data:\n",[3335,4823,4824,4830,4836],{"__ignoreMap":3675},[3138,4825,4826,4828],{"class":3136,"line":3680},[3138,4827,4217],{"class":3832},[3138,4829,3837],{"class":3836},[3138,4831,4832,4834],{"class":3136,"line":3694},[3138,4833,4554],{"class":3832},[3138,4835,3837],{"class":3836},[3138,4837,4838,4840],{"class":3136,"line":3755},[3138,4839,4562],{"class":3832},[3138,4841,3837],{"class":3836},[3114,4843,4844,4845,4848,4849,4852],{},"Compose створить named volumes ",[3335,4846,4847],{},"myapp_db-data"," та ",[3335,4850,4851],{},"myapp_wp-data"," (з префіксом назви проєкту).",[3114,4854,4855],{},[3120,4856,4857],{},"4. Networks:",[3670,4859,4861],{"className":3823,"code":4860,"language":3825,"meta":3675,"style":3675},"networks:\n  frontend:\n  backend:\n",[3335,4862,4863,4869,4875],{"__ignoreMap":3675},[3138,4864,4865,4867],{"class":3136,"line":3680},[3138,4866,4213],{"class":3832},[3138,4868,3837],{"class":3836},[3138,4870,4871,4873],{"class":3136,"line":3694},[3138,4872,4582],{"class":3832},[3138,4874,3837],{"class":3836},[3138,4876,4877,4879],{"class":3136,"line":3755},[3138,4878,4590],{"class":3832},[3138,4880,3837],{"class":3836},[3114,4882,4883,4884,4848,4887,3394],{},"Compose створить дві bridge-мережі: ",[3335,4885,4886],{},"myapp_frontend",[3335,4888,4889],{},"myapp_backend",[3114,4891,4892],{},[3398,4893],{"alt":4894,"className":4895,"src":4896},"Архітектура WordPress і MySQL у Docker Compose з frontend та backend мережами і окремими named volumes",[3402],"/images/tools/docker/docker-compose-basics/03.png",[3423,4898,4900],{"id":4899},"запуск-застосунку","Запуск застосунку",[3670,4902,4904],{"className":3672,"code":4903,"language":3674,"meta":3675,"style":3675},"# Запустити у фоновому режимі (-d = detached)\ndocker compose up -d\n",[3335,4905,4906,4911],{"__ignoreMap":3675},[3138,4907,4908],{"class":3136,"line":3680},[3138,4909,4910],{"class":3697},"# Запустити у фоновому режимі (-d = detached)\n",[3138,4912,4913,4915,4917,4920],{"class":3136,"line":3694},[3138,4914,3684],{"class":3683},[3138,4916,3688],{"class":3687},[3138,4918,4919],{"class":3687}," up",[3138,4921,4922],{"class":3725}," -d\n",[3114,4924,4925],{},[3120,4926,3895],{},[3129,4928,4930,4939,4942,4949,4956,4963,4970,4977,4984],{"title":4929},"docker compose up -d",[3133,4931,4933,3143,4936],{"className":4932},[3136],[3138,4934,3142],{"className":4935},[3141],[3120,4937,4929],{"className":4938},[3158],[3133,4940],{"className":4941},[3136],[3133,4943,4945],{"className":4944},[3136],[3138,4946,4948],{"className":4947},[3916,3158],"[+] Running 5/5",[3133,4950,4952,4953],{"className":4951},[3136]," ✔ Network myapp_frontend    ",[3138,4954,3926],{"className":4955},[3925,3158],[3133,4957,4959,4960],{"className":4958},[3136]," ✔ Network myapp_backend     ",[3138,4961,3926],{"className":4962},[3925,3158],[3133,4964,4966,4967],{"className":4965},[3136]," ✔ Volume myapp_db-data      ",[3138,4968,3926],{"className":4969},[3925,3158],[3133,4971,4973,4974],{"className":4972},[3136]," ✔ Volume myapp_wp-data      ",[3138,4975,3926],{"className":4976},[3925,3158],[3133,4978,4980,4981],{"className":4979},[3136]," ✔ Container myapp-db-1      ",[3138,4982,3934],{"className":4983},[3925,3158],[3133,4985,4987,4988],{"className":4986},[3136]," ✔ Container myapp-wordpress-1  ",[3138,4989,3934],{"className":4990},[3925,3158],[3114,4992,4993],{},[3120,4994,3996],{},[3670,4996,4998],{"className":3672,"code":4997,"language":3674,"meta":3675,"style":3675},"# Переглянути запущені сервіси\ndocker compose ps\n\n# Переглянути логи всіх сервісів\ndocker compose logs\n\n# Переглянути логи конкретного сервісу\ndocker compose logs wordpress\n\n# Слідкувати за логами в реальному часі\ndocker compose logs -f\n",[3335,4999,5000,5005,5013,5017,5022,5031,5035,5040,5052,5056,5061],{"__ignoreMap":3675},[3138,5001,5002],{"class":3136,"line":3680},[3138,5003,5004],{"class":3697},"# Переглянути запущені сервіси\n",[3138,5006,5007,5009,5011],{"class":3136,"line":3694},[3138,5008,3684],{"class":3683},[3138,5010,3688],{"class":3687},[3138,5012,4037],{"class":3687},[3138,5014,5015],{"class":3136,"line":3755},[3138,5016,3772],{"emptyLinePlaceholder":3771},[3138,5018,5019],{"class":3136,"line":3768},[3138,5020,5021],{"class":3697},"# Переглянути логи всіх сервісів\n",[3138,5023,5024,5026,5028],{"class":3136,"line":3775},[3138,5025,3684],{"class":3683},[3138,5027,3688],{"class":3687},[3138,5029,5030],{"class":3687}," logs\n",[3138,5032,5033],{"class":3136,"line":3781},[3138,5034,3772],{"emptyLinePlaceholder":3771},[3138,5036,5037],{"class":3136,"line":4165},[3138,5038,5039],{"class":3697},"# Переглянути логи конкретного сервісу\n",[3138,5041,5042,5044,5046,5049],{"class":3136,"line":4178},[3138,5043,3684],{"class":3683},[3138,5045,3688],{"class":3687},[3138,5047,5048],{"class":3687}," logs",[3138,5050,5051],{"class":3687}," wordpress\n",[3138,5053,5054],{"class":3136,"line":4189},[3138,5055,3772],{"emptyLinePlaceholder":3771},[3138,5057,5058],{"class":3136,"line":4377},[3138,5059,5060],{"class":3697},"# Слідкувати за логами в реальному часі\n",[3138,5062,5063,5065,5067,5069],{"class":3136,"line":4385},[3138,5064,3684],{"class":3683},[3138,5066,3688],{"class":3687},[3138,5068,5048],{"class":3687},[3138,5070,5071],{"class":3725}," -f\n",[3114,5073,5074],{},[3120,5075,5076],{},"Відкрити у браузері:",[3670,5078,5083],{"className":5079,"code":5081,"language":5082},[5080],"language-text","http://localhost:8080\n","text",[3335,5084,5081],{"__ignoreMap":3675},[3114,5086,5087],{},"Ви побачите інсталятор WordPress. Compose автоматично створив базу даних, підключив WordPress до неї, і все працює.",[3423,5089,5091],{"id":5090},"приклад-fastify-mongodb-mongo-express","Приклад: Fastify + MongoDB + Mongo Express",[3114,5093,5094,5095,5098],{},"Цей стек є типовим представником ",[3120,5096,5097],{},"документно-орієнтованої архітектури"," у Node.js-екосистемі. Fastify обраний замість Express не випадково: він суттєво швидший завдяки схемній валідації через JSON Schema та оптимізованому маршрутизатору, а його плагінна система забезпечує строгу інкапсуляцію залежностей. MongoDB як документна база даних природньо поєднується з JavaScript — дані зберігаються у форматі BSON, який є розширенням JSON, що усуває необхідність у маппінгу між об'єктами та рядками реляційної таблиці.",[3114,5100,5101,5102,5105],{},"Mongo Express виконує роль адміністративного вебінтерфейсу, аналогічного до phpMyAdmin для MySQL. Принципово важливо, що цей сервіс підключається лише через профіль ",[3335,5103,5104],{},"debug"," — він не повинен потрапляти у production-середовище.",[3412,5107,5108,5109,4673,5111,5113,5114,5117],{},"Починаючи з Docker Compose V2 (вбудованого у Docker Engine 20.10+), поле ",[3335,5110,4123],{},[3335,5112,3409],{}," є ",[3120,5115,5116],{},"застарілим і ігнорується",". Специфікація формату файлу більше не прив'язана до версії — Compose автоматично розпізнає всі підтримувані директиви. Видалення цього поля є рекомендованою практикою для нових проєктів.",[3114,5119,5120],{},[3120,5121,5122],{},"Структура проєкту:",[3670,5124,5127],{"className":5125,"code":5126,"language":5082},[5080],"fastify-app/\n├── docker-compose.yml\n└── app/\n    ├── package.json\n    └── index.js\n",[3335,5128,5126],{"__ignoreMap":3675},[3114,5130,5131],{},[3120,5132,5133,3820],{},[3335,5134,3409],{},[3670,5136,5139],{"className":5137,"code":5138,"language":3825,"meta":3675,"style":3675},"language-yaml shiki shiki-themes light-plus dark-plus dark-plus has-highlighted","services:\n  api:\n    image: node:20-alpine\n    working_dir: /app\n    volumes:\n      - ./app:/app\n      - /app/node_modules  # [!code highlight]\n    command: sh -c \"npm install && node index.js\"\n    ports:\n      - \"3000:3000\"\n    environment:\n      MONGO_URL: mongodb://mongo:27017/mydb\n      NODE_ENV: development\n    networks:\n      - backend\n    depends_on:\n      mongo:\n        condition: service_healthy\n\n  mongo:\n    image: mongo:7\n    volumes:\n      - mongo-data:/data/db\n    networks:\n      - backend\n    healthcheck:\n      test: [\"CMD\", \"mongosh\", \"--eval\", \"db.adminCommand('ping')\"]\n      interval: 10s\n      timeout: 5s\n      retries: 5\n\n  mongo-express:\n    image: mongo-express:latest\n    ports:\n      - \"8081:8081\"\n    environment:\n      ME_CONFIG_MONGODB_SERVER: mongo\n      ME_CONFIG_BASICAUTH_USERNAME: admin\n      ME_CONFIG_BASICAUTH_PASSWORD: admin\n    networks:\n      - backend\n    depends_on:\n      mongo:\n        condition: service_healthy\n    profiles:\n      - debug\n\nvolumes:\n  mongo-data:\n\nnetworks:\n  backend:\n",[3335,5140,5141,5147,5154,5163,5173,5179,5186,5195,5205,5211,5218,5224,5234,5244,5250,5256,5262,5269,5279,5283,5290,5299,5305,5312,5318,5324,5331,5360,5370,5380,5391,5395,5402,5411,5417,5424,5430,5440,5451,5461,5468,5475,5482,5489,5498,5506,5514,5519,5526,5534,5539,5546],{"__ignoreMap":3675},[3138,5142,5143,5145],{"class":3136,"line":3680},[3138,5144,3833],{"class":3832},[3138,5146,3837],{"class":3836},[3138,5148,5149,5152],{"class":3136,"line":3694},[3138,5150,5151],{"class":3832},"  api",[3138,5153,3837],{"class":3836},[3138,5155,5156,5158,5160],{"class":3136,"line":3755},[3138,5157,3849],{"class":3832},[3138,5159,3852],{"class":3836},[3138,5161,5162],{"class":3855},"node:20-alpine\n",[3138,5164,5165,5168,5170],{"class":3136,"line":3768},[3138,5166,5167],{"class":3832},"    working_dir",[3138,5169,3852],{"class":3836},[3138,5171,5172],{"class":3855},"/app\n",[3138,5174,5175,5177],{"class":3136,"line":3775},[3138,5176,4372],{"class":3832},[3138,5178,3837],{"class":3836},[3138,5180,5181,5183],{"class":3136,"line":3781},[3138,5182,3868],{"class":3836},[3138,5184,5185],{"class":3855},"./app:/app\n",[3138,5187,5190,5192],{"class":5188,"line":4165},[3136,5189],"highlighted",[3138,5191,3868],{"class":3836},[3138,5193,5194],{"class":3855},"/app/node_modules",[3138,5196,5197,5200,5202],{"class":3136,"line":4178},[3138,5198,5199],{"class":3832},"    command",[3138,5201,3852],{"class":3836},[3138,5203,5204],{"class":3855},"sh -c \"npm install && node index.js\"\n",[3138,5206,5207,5209],{"class":3136,"line":4189},[3138,5208,3861],{"class":3832},[3138,5210,3837],{"class":3836},[3138,5212,5213,5215],{"class":3136,"line":4377},[3138,5214,3868],{"class":3836},[3138,5216,5217],{"class":3687},"\"3000:3000\"\n",[3138,5219,5220,5222],{"class":3136,"line":4385},[3138,5221,4325],{"class":3832},[3138,5223,3837],{"class":3836},[3138,5225,5226,5229,5231],{"class":3136,"line":4393},[3138,5227,5228],{"class":3832},"      MONGO_URL",[3138,5230,3852],{"class":3836},[3138,5232,5233],{"class":3855},"mongodb://mongo:27017/mydb\n",[3138,5235,5236,5239,5241],{"class":3136,"line":4401},[3138,5237,5238],{"class":3832},"      NODE_ENV",[3138,5240,3852],{"class":3836},[3138,5242,5243],{"class":3855},"development\n",[3138,5245,5246,5248],{"class":3136,"line":4406},[3138,5247,4388],{"class":3832},[3138,5249,3837],{"class":3836},[3138,5251,5252,5254],{"class":3136,"line":4414},[3138,5253,3868],{"class":3836},[3138,5255,4398],{"class":3855},[3138,5257,5258,5260],{"class":3136,"line":4424},[3138,5259,4526],{"class":3832},[3138,5261,3837],{"class":3836},[3138,5263,5264,5267],{"class":3136,"line":4431},[3138,5265,5266],{"class":3832},"      mongo",[3138,5268,3837],{"class":3836},[3138,5270,5271,5274,5276],{"class":3136,"line":4438},[3138,5272,5273],{"class":3832},"        condition",[3138,5275,3852],{"class":3836},[3138,5277,5278],{"class":3855},"service_healthy\n",[3138,5280,5281],{"class":3136,"line":4445},[3138,5282,3772],{"emptyLinePlaceholder":3771},[3138,5284,5285,5288],{"class":3136,"line":4456},[3138,5286,5287],{"class":3832},"  mongo",[3138,5289,3837],{"class":3836},[3138,5291,5292,5294,5296],{"class":3136,"line":4466},[3138,5293,3849],{"class":3832},[3138,5295,3852],{"class":3836},[3138,5297,5298],{"class":3855},"mongo:7\n",[3138,5300,5301,5303],{"class":3136,"line":4476},[3138,5302,4372],{"class":3832},[3138,5304,3837],{"class":3836},[3138,5306,5307,5309],{"class":3136,"line":4486},[3138,5308,3868],{"class":3836},[3138,5310,5311],{"class":3855},"mongo-data:/data/db\n",[3138,5313,5314,5316],{"class":3136,"line":4493},[3138,5315,4388],{"class":3832},[3138,5317,3837],{"class":3836},[3138,5319,5320,5322],{"class":3136,"line":4501},[3138,5321,3868],{"class":3836},[3138,5323,4398],{"class":3855},[3138,5325,5326,5329],{"class":3136,"line":4508},[3138,5327,5328],{"class":3832},"    healthcheck",[3138,5330,3837],{"class":3836},[3138,5332,5333,5336,5339,5342,5344,5347,5349,5352,5354,5357],{"class":3136,"line":4515},[3138,5334,5335],{"class":3832},"      test",[3138,5337,5338],{"class":3836},": [",[3138,5340,5341],{"class":3687},"\"CMD\"",[3138,5343,4272],{"class":3836},[3138,5345,5346],{"class":3687},"\"mongosh\"",[3138,5348,4272],{"class":3836},[3138,5350,5351],{"class":3687},"\"--eval\"",[3138,5353,4272],{"class":3836},[3138,5355,5356],{"class":3687},"\"db.adminCommand('ping')\"",[3138,5358,5359],{"class":3836},"]\n",[3138,5361,5362,5365,5367],{"class":3136,"line":4523},[3138,5363,5364],{"class":3832},"      interval",[3138,5366,3852],{"class":3836},[3138,5368,5369],{"class":3855},"10s\n",[3138,5371,5372,5375,5377],{"class":3136,"line":4531},[3138,5373,5374],{"class":3832},"      timeout",[3138,5376,3852],{"class":3836},[3138,5378,5379],{"class":3855},"5s\n",[3138,5381,5382,5385,5387],{"class":3136,"line":4539},[3138,5383,5384],{"class":3832},"      retries",[3138,5386,3852],{"class":3836},[3138,5388,5390],{"class":5389},"sJj4R","5\n",[3138,5392,5393],{"class":3136,"line":4544},[3138,5394,3772],{"emptyLinePlaceholder":3771},[3138,5396,5397,5400],{"class":3136,"line":4551},[3138,5398,5399],{"class":3832},"  mongo-express",[3138,5401,3837],{"class":3836},[3138,5403,5404,5406,5408],{"class":3136,"line":4559},[3138,5405,3849],{"class":3832},[3138,5407,3852],{"class":3836},[3138,5409,5410],{"class":3855},"mongo-express:latest\n",[3138,5412,5413,5415],{"class":3136,"line":4567},[3138,5414,3861],{"class":3832},[3138,5416,3837],{"class":3836},[3138,5418,5419,5421],{"class":3136,"line":4572},[3138,5420,3868],{"class":3836},[3138,5422,5423],{"class":3687},"\"8081:8081\"\n",[3138,5425,5426,5428],{"class":3136,"line":4579},[3138,5427,4325],{"class":3832},[3138,5429,3837],{"class":3836},[3138,5431,5432,5435,5437],{"class":3136,"line":4587},[3138,5433,5434],{"class":3832},"      ME_CONFIG_MONGODB_SERVER",[3138,5436,3852],{"class":3836},[3138,5438,5439],{"class":3855},"mongo\n",[3138,5441,5443,5446,5448],{"class":3136,"line":5442},38,[3138,5444,5445],{"class":3832},"      ME_CONFIG_BASICAUTH_USERNAME",[3138,5447,3852],{"class":3836},[3138,5449,5450],{"class":3855},"admin\n",[3138,5452,5454,5457,5459],{"class":3136,"line":5453},39,[3138,5455,5456],{"class":3832},"      ME_CONFIG_BASICAUTH_PASSWORD",[3138,5458,3852],{"class":3836},[3138,5460,5450],{"class":3855},[3138,5462,5464,5466],{"class":3136,"line":5463},40,[3138,5465,4388],{"class":3832},[3138,5467,3837],{"class":3836},[3138,5469,5471,5473],{"class":3136,"line":5470},41,[3138,5472,3868],{"class":3836},[3138,5474,4398],{"class":3855},[3138,5476,5478,5480],{"class":3136,"line":5477},42,[3138,5479,4526],{"class":3832},[3138,5481,3837],{"class":3836},[3138,5483,5485,5487],{"class":3136,"line":5484},43,[3138,5486,5266],{"class":3832},[3138,5488,3837],{"class":3836},[3138,5490,5492,5494,5496],{"class":3136,"line":5491},44,[3138,5493,5273],{"class":3832},[3138,5495,3852],{"class":3836},[3138,5497,5278],{"class":3855},[3138,5499,5501,5504],{"class":3136,"line":5500},45,[3138,5502,5503],{"class":3832},"    profiles",[3138,5505,3837],{"class":3836},[3138,5507,5509,5511],{"class":3136,"line":5508},46,[3138,5510,3868],{"class":3836},[3138,5512,5513],{"class":3855},"debug\n",[3138,5515,5517],{"class":3136,"line":5516},47,[3138,5518,3772],{"emptyLinePlaceholder":3771},[3138,5520,5522,5524],{"class":3136,"line":5521},48,[3138,5523,4217],{"class":3832},[3138,5525,3837],{"class":3836},[3138,5527,5529,5532],{"class":3136,"line":5528},49,[3138,5530,5531],{"class":3832},"  mongo-data",[3138,5533,3837],{"class":3836},[3138,5535,5537],{"class":3136,"line":5536},50,[3138,5538,3772],{"emptyLinePlaceholder":3771},[3138,5540,5542,5544],{"class":3136,"line":5541},51,[3138,5543,4213],{"class":3832},[3138,5545,3837],{"class":3836},[3138,5547,5549,5551],{"class":3136,"line":5548},52,[3138,5550,4590],{"class":3832},[3138,5552,3837],{"class":3836},[3114,5554,5555,5556,5558,5559,5562,5563,5566,5567,5570,5571,5574,5575,5577],{},"Зверніть увагу на виділений рядок — анонімний volume ",[3335,5557,5194],{},". Це класичний прийом у Node.js-розробці з Docker: bind mount ",[3335,5560,5561],{},"./app:/app"," перезапише всі файли директорії ",[3335,5564,5565],{},"/app"," у контейнері вмістом хоста, включно з відсутньою папкою ",[3335,5568,5569],{},"node_modules"," (якщо вона не встановлена локально або її вміст відрізняється). Анонімний volume, оголошений ",[3120,5572,5573],{},"після"," bind mount, «виграє» у пріоритеті для конкретного шляху ",[3335,5576,5194],{}," — Docker зберігає там пакети, встановлені під час збірки контейнера, і ніколи не перезаписує їх з хоста.",[3114,5579,5580],{},[3120,5581,5582,5583,3820],{},"Змінні середовища сервісу ",[3335,5584,5585],{},"api",[4204,5587,5588,5599],{},[4207,5589,5592,5593,5596,5597,3394],{"name":5590,"type":5591},"MONGO_URL","string","Рядок підключення до MongoDB. Використовує назву сервісу ",[3335,5594,5595],{},"mongo"," як hostname — Docker DNS автоматично резолвить його у внутрішню IP-адресу контейнера всередині мережі ",[3335,5598,4701],{},[4207,5600,5602,5603,5606],{"name":5601,"type":5591},"NODE_ENV","Режим виконання Node.js. Значення ",[3335,5604,5605],{},"development"," вмикає детальне логування та вимикає деякі оптимізації продуктивності.",[3114,5608,5609],{},[3120,5610,5611,3820],{},[3335,5612,5613],{},"app/index.js",[3670,5615,5619],{"className":5616,"code":5617,"language":5618,"meta":3675,"style":3675},"language-javascript shiki shiki-themes light-plus dark-plus dark-plus","import Fastify from 'fastify'\nimport { MongoClient } from 'mongodb'\n\nconst fastify = Fastify({ logger: true })\nconst client = new MongoClient(process.env.MONGO_URL)\n\nawait client.connect()\nconst db = client.db()\n\nfastify.get('/', async () => {\n  const count = await db.collection('visits').countDocuments()\n  await db.collection('visits').insertOne({ at: new Date() })\n  return { message: 'Hello from Fastify!', visits: count + 1 }\n})\n\nawait fastify.listen({ port: 3000, host: '0.0.0.0' })\n","javascript",[3335,5620,5621,5637,5656,5660,5687,5720,5724,5739,5757,5761,5790,5822,5856,5885,5890,5894],{"__ignoreMap":3675},[3138,5622,5623,5627,5631,5634],{"class":3136,"line":3680},[3138,5624,5626],{"class":5625},"sCDza","import",[3138,5628,5630],{"class":5629},"siwwj"," Fastify",[3138,5632,5633],{"class":5625}," from",[3138,5635,5636],{"class":3687}," 'fastify'\n",[3138,5638,5639,5641,5644,5647,5650,5653],{"class":3136,"line":3694},[3138,5640,5626],{"class":5625},[3138,5642,5643],{"class":3836}," { ",[3138,5645,5646],{"class":5629},"MongoClient",[3138,5648,5649],{"class":3836}," } ",[3138,5651,5652],{"class":5625},"from",[3138,5654,5655],{"class":3687}," 'mongodb'\n",[3138,5657,5658],{"class":3136,"line":3755},[3138,5659,3772],{"emptyLinePlaceholder":3771},[3138,5661,5662,5665,5669,5672,5675,5678,5681,5684],{"class":3136,"line":3768},[3138,5663,5664],{"class":3725},"const",[3138,5666,5668],{"class":5667},"s-QsJ"," fastify",[3138,5670,5671],{"class":3836}," = ",[3138,5673,5674],{"class":3683},"Fastify",[3138,5676,5677],{"class":3836},"({ ",[3138,5679,5680],{"class":5629},"logger:",[3138,5682,5683],{"class":3725}," true",[3138,5685,5686],{"class":3836}," })\n",[3138,5688,5689,5691,5694,5696,5699,5702,5705,5708,5710,5713,5715,5717],{"class":3136,"line":3775},[3138,5690,5664],{"class":3725},[3138,5692,5693],{"class":5667}," client",[3138,5695,5671],{"class":3836},[3138,5697,5698],{"class":3725},"new",[3138,5700,5701],{"class":3683}," MongoClient",[3138,5703,5704],{"class":3836},"(",[3138,5706,5707],{"class":5629},"process",[3138,5709,3394],{"class":3836},[3138,5711,5712],{"class":5629},"env",[3138,5714,3394],{"class":3836},[3138,5716,5590],{"class":5667},[3138,5718,5719],{"class":3836},")\n",[3138,5721,5722],{"class":3136,"line":3781},[3138,5723,3772],{"emptyLinePlaceholder":3771},[3138,5725,5726,5729,5731,5733,5736],{"class":3136,"line":4165},[3138,5727,5728],{"class":5625},"await",[3138,5730,5693],{"class":5629},[3138,5732,3394],{"class":3836},[3138,5734,5735],{"class":3683},"connect",[3138,5737,5738],{"class":3836},"()\n",[3138,5740,5741,5743,5746,5748,5751,5753,5755],{"class":3136,"line":4178},[3138,5742,5664],{"class":3725},[3138,5744,5745],{"class":5667}," db",[3138,5747,5671],{"class":3836},[3138,5749,5750],{"class":5629},"client",[3138,5752,3394],{"class":3836},[3138,5754,4605],{"class":3683},[3138,5756,5738],{"class":3836},[3138,5758,5759],{"class":3136,"line":4189},[3138,5760,3772],{"emptyLinePlaceholder":3771},[3138,5762,5763,5766,5768,5771,5773,5776,5778,5781,5784,5787],{"class":3136,"line":4377},[3138,5764,5765],{"class":5629},"fastify",[3138,5767,3394],{"class":3836},[3138,5769,5770],{"class":3683},"get",[3138,5772,5704],{"class":3836},[3138,5774,5775],{"class":3687},"'/'",[3138,5777,4272],{"class":3836},[3138,5779,5780],{"class":3725},"async",[3138,5782,5783],{"class":3836}," () ",[3138,5785,5786],{"class":3725},"=>",[3138,5788,5789],{"class":3836}," {\n",[3138,5791,5792,5795,5798,5800,5802,5804,5806,5809,5811,5814,5817,5820],{"class":3136,"line":4385},[3138,5793,5794],{"class":3725},"  const",[3138,5796,5797],{"class":5667}," count",[3138,5799,5671],{"class":3836},[3138,5801,5728],{"class":5625},[3138,5803,5745],{"class":5629},[3138,5805,3394],{"class":3836},[3138,5807,5808],{"class":3683},"collection",[3138,5810,5704],{"class":3836},[3138,5812,5813],{"class":3687},"'visits'",[3138,5815,5816],{"class":3836},").",[3138,5818,5819],{"class":3683},"countDocuments",[3138,5821,5738],{"class":3836},[3138,5823,5824,5827,5829,5831,5833,5835,5837,5839,5842,5844,5847,5850,5853],{"class":3136,"line":4393},[3138,5825,5826],{"class":5625},"  await",[3138,5828,5745],{"class":5629},[3138,5830,3394],{"class":3836},[3138,5832,5808],{"class":3683},[3138,5834,5704],{"class":3836},[3138,5836,5813],{"class":3687},[3138,5838,5816],{"class":3836},[3138,5840,5841],{"class":3683},"insertOne",[3138,5843,5677],{"class":3836},[3138,5845,5846],{"class":5629},"at:",[3138,5848,5849],{"class":3725}," new",[3138,5851,5852],{"class":3683}," Date",[3138,5854,5855],{"class":3836},"() })\n",[3138,5857,5858,5861,5863,5866,5869,5871,5874,5876,5879,5882],{"class":3136,"line":4401},[3138,5859,5860],{"class":5625},"  return",[3138,5862,5643],{"class":3836},[3138,5864,5865],{"class":5629},"message:",[3138,5867,5868],{"class":3687}," 'Hello from Fastify!'",[3138,5870,4272],{"class":3836},[3138,5872,5873],{"class":5629},"visits:",[3138,5875,5797],{"class":5629},[3138,5877,5878],{"class":3836}," + ",[3138,5880,5881],{"class":5389},"1",[3138,5883,5884],{"class":3836}," }\n",[3138,5886,5887],{"class":3136,"line":4406},[3138,5888,5889],{"class":3836},"})\n",[3138,5891,5892],{"class":3136,"line":4414},[3138,5893,3772],{"emptyLinePlaceholder":3771},[3138,5895,5896,5898,5900,5902,5905,5907,5910,5913,5915,5918,5921],{"class":3136,"line":4424},[3138,5897,5728],{"class":5625},[3138,5899,5668],{"class":5629},[3138,5901,3394],{"class":3836},[3138,5903,5904],{"class":3683},"listen",[3138,5906,5677],{"class":3836},[3138,5908,5909],{"class":5629},"port:",[3138,5911,5912],{"class":5389}," 3000",[3138,5914,4272],{"class":3836},[3138,5916,5917],{"class":5629},"host:",[3138,5919,5920],{"class":3687}," '0.0.0.0'",[3138,5922,5686],{"class":3836},[5924,5925,5926,5927,5930,5931,5934,5935,5938],"warning",{},"Прослуховування на ",[3335,5928,5929],{},"0.0.0.0"," є обов'язковим у контейнері. За замовчуванням Fastify (як і більшість фреймворків) прив'язується до ",[3335,5932,5933],{},"127.0.0.1"," — адреси loopback-інтерфейсу, яка недоступна ззовні контейнера. Якщо залишити ",[3335,5936,5937],{},"localhost",", застосунок буде недосяжним навіть після проброса порту.",[3114,5940,5941],{},[3120,5942,5943,3820],{},[3335,5944,5945],{},"app/package.json",[3670,5947,5951],{"className":5948,"code":5949,"language":5950,"meta":3675,"style":3675},"language-json shiki shiki-themes light-plus dark-plus dark-plus","{\n  \"name\": \"fastify-mongo-demo\",\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"fastify\": \"^4.28.1\",\n    \"mongodb\": \"^6.6.0\"\n  }\n}\n","json",[3335,5952,5953,5958,5972,5984,5992,6004,6014,6019],{"__ignoreMap":3675},[3138,5954,5955],{"class":3136,"line":3680},[3138,5956,5957],{"class":3836},"{\n",[3138,5959,5960,5964,5966,5969],{"class":3136,"line":3694},[3138,5961,5963],{"class":5962},"sLwNe","  \"name\"",[3138,5965,3852],{"class":3836},[3138,5967,5968],{"class":3687},"\"fastify-mongo-demo\"",[3138,5970,5971],{"class":3836},",\n",[3138,5973,5974,5977,5979,5982],{"class":3136,"line":3755},[3138,5975,5976],{"class":5962},"  \"type\"",[3138,5978,3852],{"class":3836},[3138,5980,5981],{"class":3687},"\"module\"",[3138,5983,5971],{"class":3836},[3138,5985,5986,5989],{"class":3136,"line":3768},[3138,5987,5988],{"class":5962},"  \"dependencies\"",[3138,5990,5991],{"class":3836},": {\n",[3138,5993,5994,5997,5999,6002],{"class":3136,"line":3775},[3138,5995,5996],{"class":5962},"    \"fastify\"",[3138,5998,3852],{"class":3836},[3138,6000,6001],{"class":3687},"\"^4.28.1\"",[3138,6003,5971],{"class":3836},[3138,6005,6006,6009,6011],{"class":3136,"line":3781},[3138,6007,6008],{"class":5962},"    \"mongodb\"",[3138,6010,3852],{"class":3836},[3138,6012,6013],{"class":3687},"\"^6.6.0\"\n",[3138,6015,6016],{"class":3136,"line":4165},[3138,6017,6018],{"class":3836},"  }\n",[3138,6020,6021],{"class":3136,"line":4178},[3138,6022,6023],{"class":3836},"}\n",[3114,6025,6026],{},[3120,6027,6028],{},"Запуск та перевірка:",[6030,6031,6032,6036,6051,6066,6070,6087,6090,6094],"steps",{},[3423,6033,6035],{"id":6034},"запустити-стек","Запустити стек",[3670,6037,6039],{"className":3672,"code":6038,"language":3674,"meta":3675,"style":3675},"docker compose up -d\n",[3335,6040,6041],{"__ignoreMap":3675},[3138,6042,6043,6045,6047,6049],{"class":3136,"line":3680},[3138,6044,3684],{"class":3683},[3138,6046,3688],{"class":3687},[3138,6048,4919],{"class":3687},[3138,6050,4922],{"class":3725},[3114,6052,6053,6054,6056,6057,6060,6061,6063,6064,3394],{},"Compose послідовно: 1) створить мережу ",[3335,6055,4701],{}," та volume ",[3335,6058,6059],{},"mongo-data",", 2) запустить ",[3335,6062,5595],{}," та дочекається успішного health check, 3) лише потім запустить ",[3335,6065,5585],{},[3423,6067,6069],{"id":6068},"перевірити-відповідь-api","Перевірити відповідь API",[3670,6071,6073],{"className":3672,"code":6072,"language":3674,"meta":3675,"style":3675},"curl http://localhost:3000\n# {\"message\":\"Hello from Fastify!\",\"visits\":1}\n",[3335,6074,6075,6082],{"__ignoreMap":3675},[3138,6076,6077,6079],{"class":3136,"line":3680},[3138,6078,4011],{"class":3683},[3138,6080,6081],{"class":3687}," http://localhost:3000\n",[3138,6083,6084],{"class":3136,"line":3694},[3138,6085,6086],{"class":3697},"# {\"message\":\"Hello from Fastify!\",\"visits\":1}\n",[3114,6088,6089],{},"Кожен виклик збільшує лічильник — дані зберігаються у MongoDB між запитами.",[3423,6091,6093],{"id":6092},"відкрити-mongo-express-опціонально","Відкрити Mongo Express (опціонально)",[3670,6095,6097],{"className":3672,"code":6096,"language":3674,"meta":3675,"style":3675},"docker compose --profile debug up -d mongo-express\nopen http://localhost:8081\n# Логін: admin / admin\n",[3335,6098,6099,6119,6127],{"__ignoreMap":3675},[3138,6100,6101,6103,6105,6108,6111,6113,6116],{"class":3136,"line":3680},[3138,6102,3684],{"class":3683},[3138,6104,3688],{"class":3687},[3138,6106,6107],{"class":3725}," --profile",[3138,6109,6110],{"class":3687}," debug",[3138,6112,4919],{"class":3687},[3138,6114,6115],{"class":3725}," -d",[3138,6117,6118],{"class":3687}," mongo-express\n",[3138,6120,6121,6124],{"class":3136,"line":3694},[3138,6122,6123],{"class":3683},"open",[3138,6125,6126],{"class":3687}," http://localhost:8081\n",[3138,6128,6129],{"class":3136,"line":3755},[3138,6130,6131],{"class":3697},"# Логін: admin / admin\n",[3641,6133,6134,6137,6138,6141,6142,6145,6146,6149,6150,6153,6154,6157],{},[3120,6135,6136],{},"Hot-reload для розробки:"," Замініть ",[3335,6139,6140],{},"node index.js"," на ",[3335,6143,6144],{},"npx nodemon index.js"," у полі ",[3335,6147,6148],{},"command"," — Fastify перезапускатиметься автоматично при кожній зміні файлів у ",[3335,6151,6152],{},"./app",". Nodemon встановлювати окремо не потрібно — ",[3335,6155,6156],{},"npx"," завантажить його тимчасово.",[3416,6159],{},[3423,6161,6163],{"id":6162},"приклад-aspnet-core-postgresql-pgadmin","Приклад: ASP.NET Core + PostgreSQL + pgAdmin",[3114,6165,6166],{},"Цей стек представляє канонічну архітектуру серверного застосунку у C#-екосистемі. ASP.NET Core 8 Minimal API надає лаконічний спосіб визначення HTTP-ендпоінтів без церемоніального шаблонного коду контролерів, тоді як Entity Framework Core виконує роль ORM-прошарку між доменними об'єктами та реляційною схемою PostgreSQL.",[3114,6168,6169,6170,6173,6174,6177,6178,6180],{},"З точки зору оркестрації через Compose цей приклад демонструє важливий патерн: ",[3335,6171,6172],{},"restart: on-failure"," у поєднанні з ",[3335,6175,6176],{},"condition: service_healthy",". Навіть коли health check PostgreSQL пройшов успішно, ASP.NET Core потребує певного часу для виконання міграцій EF Core та прогріву Dependency Injection-контейнера. Якщо з якоїсь причини стартова послідовність порушиться — Compose автоматично перезапустить сервіс ",[3335,6179,5585],{},", не вимагаючи ручного втручання.",[3114,6182,6183],{},[3120,6184,5122],{},[6186,6187,6188,6291,6853,7270],"code-tree",{},[3670,6189,6194],{"className":6190,"code":6191,"filename":6192,"language":6193,"meta":3675,"style":3675},"language-dockerfile shiki shiki-themes light-plus dark-plus dark-plus","FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build\nWORKDIR /src\nCOPY *.csproj .\nRUN dotnet restore\nCOPY . .\nRUN dotnet publish -c Release -o /app\n\nFROM mcr.microsoft.com/dotnet/aspnet:8.0\nWORKDIR /app\nCOPY --from=build /app .\nENTRYPOINT [\"dotnet\", \"MyApi.dll\"]\n","api/Dockerfile","dockerfile",[3335,6195,6196,6210,6218,6226,6234,6241,6248,6252,6259,6266,6273],{"__ignoreMap":3675},[3138,6197,6198,6201,6204,6207],{"class":3136,"line":3680},[3138,6199,6200],{"class":3725},"FROM",[3138,6202,6203],{"class":3836}," mcr.microsoft.com/dotnet/sdk:8.0 ",[3138,6205,6206],{"class":3725},"AS",[3138,6208,6209],{"class":3836}," build\n",[3138,6211,6212,6215],{"class":3136,"line":3694},[3138,6213,6214],{"class":3725},"WORKDIR",[3138,6216,6217],{"class":3836}," /src\n",[3138,6219,6220,6223],{"class":3136,"line":3755},[3138,6221,6222],{"class":3725},"COPY",[3138,6224,6225],{"class":3836}," *.csproj .\n",[3138,6227,6228,6231],{"class":3136,"line":3768},[3138,6229,6230],{"class":3725},"RUN",[3138,6232,6233],{"class":3836}," dotnet restore\n",[3138,6235,6236,6238],{"class":3136,"line":3775},[3138,6237,6222],{"class":3725},[3138,6239,6240],{"class":3836}," . .\n",[3138,6242,6243,6245],{"class":3136,"line":3781},[3138,6244,6230],{"class":3725},[3138,6246,6247],{"class":3836}," dotnet publish -c Release -o /app\n",[3138,6249,6250],{"class":3136,"line":4165},[3138,6251,3772],{"emptyLinePlaceholder":3771},[3138,6253,6254,6256],{"class":3136,"line":4178},[3138,6255,6200],{"class":3725},[3138,6257,6258],{"class":3836}," mcr.microsoft.com/dotnet/aspnet:8.0\n",[3138,6260,6261,6263],{"class":3136,"line":4189},[3138,6262,6214],{"class":3725},[3138,6264,6265],{"class":3836}," /app\n",[3138,6267,6268,6270],{"class":3136,"line":4377},[3138,6269,6222],{"class":3725},[3138,6271,6272],{"class":3836}," --from=build /app .\n",[3138,6274,6275,6278,6281,6284,6286,6289],{"class":3136,"line":4385},[3138,6276,6277],{"class":3725},"ENTRYPOINT",[3138,6279,6280],{"class":3836}," [",[3138,6282,6283],{"class":3687},"\"dotnet\"",[3138,6285,4272],{"class":3836},[3138,6287,6288],{"class":3687},"\"MyApi.dll\"",[3138,6290,5359],{"class":3836},[3670,6292,6297],{"className":6293,"code":6294,"filename":6295,"language":6296,"meta":3675,"style":3675},"language-csharp shiki shiki-themes light-plus dark-plus dark-plus","using Microsoft.EntityFrameworkCore;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddDbContext\u003CAppDbContext>(options =>\n    options.UseNpgsql(builder.Configuration.GetConnectionString(\"DefaultConnection\")));\n\nvar app = builder.Build();\n\n// Автоматично застосувати міграції при старті\nusing (var scope = app.Services.CreateScope())\n{\n    var db = scope.ServiceProvider.GetRequiredService\u003CAppDbContext>();\n    db.Database.Migrate();\n}\n\napp.MapGet(\"/\", () => \"ASP.NET Core + PostgreSQL via Docker Compose!\");\n\napp.MapGet(\"/products\", async (AppDbContext db) =>\n    await db.Products.ToListAsync());\n\napp.MapPost(\"/products\", async (Product product, AppDbContext db) =>\n{\n    db.Products.Add(product);\n    await db.SaveChangesAsync();\n    return Results.Created($\"/products/{product.Id}\", product);\n});\n\napp.Run();\n\n// --- Models ---\nrecord Product(int Id, string Name, decimal Price);\n\nclass AppDbContext(DbContextOptions\u003CAppDbContext> options) : DbContext(options)\n{\n    public DbSet\u003CProduct> Products => Set\u003CProduct>();\n}\n","api/Program.cs","csharp",[3335,6298,6299,6316,6320,6346,6350,6380,6412,6416,6435,6439,6444,6473,6477,6506,6523,6527,6531,6553,6557,6583,6603,6607,6640,6644,6664,6677,6717,6722,6726,6737,6741,6746,6779,6783,6817,6821,6849],{"__ignoreMap":3675},[3138,6300,6301,6304,6308,6310,6313],{"class":3136,"line":3680},[3138,6302,6303],{"class":5625},"using",[3138,6305,6307],{"class":6306},"sN1BT"," Microsoft",[3138,6309,3394],{"class":3836},[3138,6311,6312],{"class":6306},"EntityFrameworkCore",[3138,6314,6315],{"class":3836},";\n",[3138,6317,6318],{"class":3136,"line":3694},[3138,6319,3772],{"emptyLinePlaceholder":3771},[3138,6321,6322,6325,6328,6330,6333,6335,6338,6340,6343],{"class":3136,"line":3755},[3138,6323,6324],{"class":3725},"var",[3138,6326,6327],{"class":5629}," builder",[3138,6329,5671],{"class":3836},[3138,6331,6332],{"class":5629},"WebApplication",[3138,6334,3394],{"class":3836},[3138,6336,6337],{"class":3683},"CreateBuilder",[3138,6339,5704],{"class":3836},[3138,6341,6342],{"class":5629},"args",[3138,6344,6345],{"class":3836},");\n",[3138,6347,6348],{"class":3136,"line":3768},[3138,6349,3772],{"emptyLinePlaceholder":3771},[3138,6351,6352,6355,6357,6360,6362,6365,6368,6371,6374,6377],{"class":3136,"line":3775},[3138,6353,6354],{"class":5629},"builder",[3138,6356,3394],{"class":3836},[3138,6358,6359],{"class":5629},"Services",[3138,6361,3394],{"class":3836},[3138,6363,6364],{"class":3683},"AddDbContext",[3138,6366,6367],{"class":3836},"\u003C",[3138,6369,6370],{"class":6306},"AppDbContext",[3138,6372,6373],{"class":3836},">(",[3138,6375,6376],{"class":5629},"options",[3138,6378,6379],{"class":3836}," =>\n",[3138,6381,6382,6385,6387,6390,6392,6394,6396,6399,6401,6404,6406,6409],{"class":3136,"line":3781},[3138,6383,6384],{"class":5629},"    options",[3138,6386,3394],{"class":3836},[3138,6388,6389],{"class":3683},"UseNpgsql",[3138,6391,5704],{"class":3836},[3138,6393,6354],{"class":5629},[3138,6395,3394],{"class":3836},[3138,6397,6398],{"class":5629},"Configuration",[3138,6400,3394],{"class":3836},[3138,6402,6403],{"class":3683},"GetConnectionString",[3138,6405,5704],{"class":3836},[3138,6407,6408],{"class":3687},"\"DefaultConnection\"",[3138,6410,6411],{"class":3836},")));\n",[3138,6413,6414],{"class":3136,"line":4165},[3138,6415,3772],{"emptyLinePlaceholder":3771},[3138,6417,6418,6420,6423,6425,6427,6429,6432],{"class":3136,"line":4178},[3138,6419,6324],{"class":3725},[3138,6421,6422],{"class":5629}," app",[3138,6424,5671],{"class":3836},[3138,6426,6354],{"class":5629},[3138,6428,3394],{"class":3836},[3138,6430,6431],{"class":3683},"Build",[3138,6433,6434],{"class":3836},"();\n",[3138,6436,6437],{"class":3136,"line":4189},[3138,6438,3772],{"emptyLinePlaceholder":3771},[3138,6440,6441],{"class":3136,"line":4377},[3138,6442,6443],{"class":3697},"// Автоматично застосувати міграції при старті\n",[3138,6445,6446,6448,6451,6453,6456,6458,6461,6463,6465,6467,6470],{"class":3136,"line":4385},[3138,6447,6303],{"class":5625},[3138,6449,6450],{"class":3836}," (",[3138,6452,6324],{"class":3725},[3138,6454,6455],{"class":5629}," scope",[3138,6457,5671],{"class":3836},[3138,6459,6460],{"class":5629},"app",[3138,6462,3394],{"class":3836},[3138,6464,6359],{"class":5629},[3138,6466,3394],{"class":3836},[3138,6468,6469],{"class":3683},"CreateScope",[3138,6471,6472],{"class":3836},"())\n",[3138,6474,6475],{"class":3136,"line":4393},[3138,6476,5957],{"class":3836},[3138,6478,6479,6482,6484,6486,6489,6491,6494,6496,6499,6501,6503],{"class":3136,"line":4401},[3138,6480,6481],{"class":3725},"    var",[3138,6483,5745],{"class":5629},[3138,6485,5671],{"class":3836},[3138,6487,6488],{"class":5629},"scope",[3138,6490,3394],{"class":3836},[3138,6492,6493],{"class":5629},"ServiceProvider",[3138,6495,3394],{"class":3836},[3138,6497,6498],{"class":3683},"GetRequiredService",[3138,6500,6367],{"class":3836},[3138,6502,6370],{"class":6306},[3138,6504,6505],{"class":3836},">();\n",[3138,6507,6508,6511,6513,6516,6518,6521],{"class":3136,"line":4406},[3138,6509,6510],{"class":5629},"    db",[3138,6512,3394],{"class":3836},[3138,6514,6515],{"class":5629},"Database",[3138,6517,3394],{"class":3836},[3138,6519,6520],{"class":3683},"Migrate",[3138,6522,6434],{"class":3836},[3138,6524,6525],{"class":3136,"line":4414},[3138,6526,6023],{"class":3836},[3138,6528,6529],{"class":3136,"line":4424},[3138,6530,3772],{"emptyLinePlaceholder":3771},[3138,6532,6533,6535,6537,6540,6542,6545,6548,6551],{"class":3136,"line":4431},[3138,6534,6460],{"class":5629},[3138,6536,3394],{"class":3836},[3138,6538,6539],{"class":3683},"MapGet",[3138,6541,5704],{"class":3836},[3138,6543,6544],{"class":3687},"\"/\"",[3138,6546,6547],{"class":3836},", () => ",[3138,6549,6550],{"class":3687},"\"ASP.NET Core + PostgreSQL via Docker Compose!\"",[3138,6552,6345],{"class":3836},[3138,6554,6555],{"class":3136,"line":4438},[3138,6556,3772],{"emptyLinePlaceholder":3771},[3138,6558,6559,6561,6563,6565,6567,6570,6572,6574,6576,6578,6580],{"class":3136,"line":4445},[3138,6560,6460],{"class":5629},[3138,6562,3394],{"class":3836},[3138,6564,6539],{"class":3683},[3138,6566,5704],{"class":3836},[3138,6568,6569],{"class":3687},"\"/products\"",[3138,6571,4272],{"class":3836},[3138,6573,5780],{"class":3725},[3138,6575,6450],{"class":3836},[3138,6577,6370],{"class":6306},[3138,6579,5745],{"class":5629},[3138,6581,6582],{"class":3836},") =>\n",[3138,6584,6585,6588,6590,6592,6595,6597,6600],{"class":3136,"line":4456},[3138,6586,6587],{"class":3725},"    await",[3138,6589,5745],{"class":5629},[3138,6591,3394],{"class":3836},[3138,6593,6594],{"class":5629},"Products",[3138,6596,3394],{"class":3836},[3138,6598,6599],{"class":3683},"ToListAsync",[3138,6601,6602],{"class":3836},"());\n",[3138,6604,6605],{"class":3136,"line":4466},[3138,6606,3772],{"emptyLinePlaceholder":3771},[3138,6608,6609,6611,6613,6616,6618,6620,6622,6624,6626,6629,6632,6634,6636,6638],{"class":3136,"line":4476},[3138,6610,6460],{"class":5629},[3138,6612,3394],{"class":3836},[3138,6614,6615],{"class":3683},"MapPost",[3138,6617,5704],{"class":3836},[3138,6619,6569],{"class":3687},[3138,6621,4272],{"class":3836},[3138,6623,5780],{"class":3725},[3138,6625,6450],{"class":3836},[3138,6627,6628],{"class":6306},"Product",[3138,6630,6631],{"class":5629}," product",[3138,6633,4272],{"class":3836},[3138,6635,6370],{"class":6306},[3138,6637,5745],{"class":5629},[3138,6639,6582],{"class":3836},[3138,6641,6642],{"class":3136,"line":4486},[3138,6643,5957],{"class":3836},[3138,6645,6646,6648,6650,6652,6654,6657,6659,6662],{"class":3136,"line":4493},[3138,6647,6510],{"class":5629},[3138,6649,3394],{"class":3836},[3138,6651,6594],{"class":5629},[3138,6653,3394],{"class":3836},[3138,6655,6656],{"class":3683},"Add",[3138,6658,5704],{"class":3836},[3138,6660,6661],{"class":5629},"product",[3138,6663,6345],{"class":3836},[3138,6665,6666,6668,6670,6672,6675],{"class":3136,"line":4501},[3138,6667,6587],{"class":3725},[3138,6669,5745],{"class":5629},[3138,6671,3394],{"class":3836},[3138,6673,6674],{"class":3683},"SaveChangesAsync",[3138,6676,6434],{"class":3836},[3138,6678,6679,6682,6685,6687,6689,6691,6694,6698,6700,6702,6705,6708,6711,6713,6715],{"class":3136,"line":4508},[3138,6680,6681],{"class":5625},"    return",[3138,6683,6684],{"class":5629}," Results",[3138,6686,3394],{"class":3836},[3138,6688,3926],{"class":3683},[3138,6690,5704],{"class":3836},[3138,6692,6693],{"class":3687},"$\"/products/",[3138,6695,6697],{"class":6696},"sD7JJ","{",[3138,6699,6661],{"class":5629},[3138,6701,3394],{"class":6696},[3138,6703,6704],{"class":5629},"Id",[3138,6706,6707],{"class":6696},"}",[3138,6709,6710],{"class":3687},"\"",[3138,6712,4272],{"class":3836},[3138,6714,6661],{"class":5629},[3138,6716,6345],{"class":3836},[3138,6718,6719],{"class":3136,"line":4515},[3138,6720,6721],{"class":3836},"});\n",[3138,6723,6724],{"class":3136,"line":4523},[3138,6725,3772],{"emptyLinePlaceholder":3771},[3138,6727,6728,6730,6732,6735],{"class":3136,"line":4531},[3138,6729,6460],{"class":5629},[3138,6731,3394],{"class":3836},[3138,6733,6734],{"class":3683},"Run",[3138,6736,6434],{"class":3836},[3138,6738,6739],{"class":3136,"line":4539},[3138,6740,3772],{"emptyLinePlaceholder":3771},[3138,6742,6743],{"class":3136,"line":4544},[3138,6744,6745],{"class":3697},"// --- Models ---\n",[3138,6747,6748,6751,6754,6756,6759,6762,6764,6766,6769,6771,6774,6777],{"class":3136,"line":4551},[3138,6749,6750],{"class":3725},"record",[3138,6752,6753],{"class":6306}," Product",[3138,6755,5704],{"class":3836},[3138,6757,6758],{"class":3725},"int",[3138,6760,6761],{"class":5629}," Id",[3138,6763,4272],{"class":3836},[3138,6765,5591],{"class":3725},[3138,6767,6768],{"class":5629}," Name",[3138,6770,4272],{"class":3836},[3138,6772,6773],{"class":3725},"decimal",[3138,6775,6776],{"class":5629}," Price",[3138,6778,6345],{"class":3836},[3138,6780,6781],{"class":3136,"line":4559},[3138,6782,3772],{"emptyLinePlaceholder":3771},[3138,6784,6785,6788,6791,6793,6796,6798,6800,6803,6805,6808,6811,6813,6815],{"class":3136,"line":4567},[3138,6786,6787],{"class":3725},"class",[3138,6789,6790],{"class":6306}," AppDbContext",[3138,6792,5704],{"class":3836},[3138,6794,6795],{"class":6306},"DbContextOptions",[3138,6797,6367],{"class":3836},[3138,6799,6370],{"class":6306},[3138,6801,6802],{"class":3836},"> ",[3138,6804,6376],{"class":5629},[3138,6806,6807],{"class":3836},") : ",[3138,6809,6810],{"class":6306},"DbContext",[3138,6812,5704],{"class":3836},[3138,6814,6376],{"class":6306},[3138,6816,5719],{"class":3836},[3138,6818,6819],{"class":3136,"line":4572},[3138,6820,5957],{"class":3836},[3138,6822,6823,6826,6829,6831,6833,6835,6837,6840,6843,6845,6847],{"class":3136,"line":4579},[3138,6824,6825],{"class":3725},"    public",[3138,6827,6828],{"class":6306}," DbSet",[3138,6830,6367],{"class":3836},[3138,6832,6628],{"class":6306},[3138,6834,6802],{"class":3836},[3138,6836,6594],{"class":5629},[3138,6838,6839],{"class":3836}," => ",[3138,6841,6842],{"class":3683},"Set",[3138,6844,6367],{"class":3836},[3138,6846,6628],{"class":6306},[3138,6848,6505],{"class":3836},[3138,6850,6851],{"class":3136,"line":4587},[3138,6852,6023],{"class":3836},[3670,6854,6856],{"className":3823,"code":6855,"filename":3409,"language":3825,"meta":3675,"style":3675},"services:\n  api:\n    build:\n      context: ./api\n      dockerfile: Dockerfile\n    ports:\n      - \"5000:8080\"\n    environment:\n      ASPNETCORE_ENVIRONMENT: Development\n      ConnectionStrings__DefaultConnection: >-\n        Host=db;Port=5432;Database=${POSTGRES_DB:-myapp};\n        Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-secret}\n    networks:\n      - backend\n    depends_on:\n      db:\n        condition: service_healthy\n    restart: on-failure\n\n  db:\n    image: postgres:16-alpine\n    environment:\n      POSTGRES_DB: ${POSTGRES_DB:-myapp}\n      POSTGRES_USER: ${POSTGRES_USER:-postgres}\n      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-secret}\n    volumes:\n      - pg-data:/var/lib/postgresql/data\n    networks:\n      - backend\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-postgres}\"]\n      interval: 10s\n      timeout: 5s\n      retries: 5\n\n  pgadmin:\n    image: dpage/pgadmin4:latest\n    ports:\n      - \"5050:80\"\n    environment:\n      PGADMIN_DEFAULT_EMAIL: admin@admin.com\n      PGADMIN_DEFAULT_PASSWORD: admin\n    volumes:\n      - pgadmin-data:/var/lib/pgadmin\n    networks:\n      - backend\n    depends_on:\n      - db\n    profiles:\n      - debug\n\nvolumes:\n  pg-data:\n  pgadmin-data:\n\nnetworks:\n  backend:\n",[3335,6857,6858,6864,6870,6877,6887,6897,6903,6910,6916,6926,6939,6944,6949,6955,6961,6967,6974,6982,6992,6996,7002,7011,7017,7027,7037,7047,7053,7060,7066,7072,7078,7094,7102,7110,7118,7122,7129,7138,7144,7151,7157,7167,7176,7182,7189,7195,7201,7207,7213,7219,7225,7229,7235,7243,7251,7256,7263],{"__ignoreMap":3675},[3138,6859,6860,6862],{"class":3136,"line":3680},[3138,6861,3833],{"class":3832},[3138,6863,3837],{"class":3836},[3138,6865,6866,6868],{"class":3136,"line":3694},[3138,6867,5151],{"class":3832},[3138,6869,3837],{"class":3836},[3138,6871,6872,6875],{"class":3136,"line":3755},[3138,6873,6874],{"class":3832},"    build",[3138,6876,3837],{"class":3836},[3138,6878,6879,6882,6884],{"class":3136,"line":3768},[3138,6880,6881],{"class":3832},"      context",[3138,6883,3852],{"class":3836},[3138,6885,6886],{"class":3855},"./api\n",[3138,6888,6889,6892,6894],{"class":3136,"line":3775},[3138,6890,6891],{"class":3832},"      dockerfile",[3138,6893,3852],{"class":3836},[3138,6895,6896],{"class":3855},"Dockerfile\n",[3138,6898,6899,6901],{"class":3136,"line":3781},[3138,6900,3861],{"class":3832},[3138,6902,3837],{"class":3836},[3138,6904,6905,6907],{"class":3136,"line":4165},[3138,6906,3868],{"class":3836},[3138,6908,6909],{"class":3687},"\"5000:8080\"\n",[3138,6911,6912,6914],{"class":3136,"line":4178},[3138,6913,4325],{"class":3832},[3138,6915,3837],{"class":3836},[3138,6917,6918,6921,6923],{"class":3136,"line":4189},[3138,6919,6920],{"class":3832},"      ASPNETCORE_ENVIRONMENT",[3138,6922,3852],{"class":3836},[3138,6924,6925],{"class":3855},"Development\n",[3138,6927,6928,6931,6933,6936],{"class":3136,"line":4377},[3138,6929,6930],{"class":3832},"      ConnectionStrings__DefaultConnection",[3138,6932,3852],{"class":3836},[3138,6934,6935],{"class":5625},">",[3138,6937,6938],{"class":3725},"-\n",[3138,6940,6941],{"class":3136,"line":4385},[3138,6942,6943],{"class":3855},"        Host=db;Port=5432;Database=${POSTGRES_DB:-myapp};\n",[3138,6945,6946],{"class":3136,"line":4393},[3138,6947,6948],{"class":3855},"        Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-secret}\n",[3138,6950,6951,6953],{"class":3136,"line":4401},[3138,6952,4388],{"class":3832},[3138,6954,3837],{"class":3836},[3138,6956,6957,6959],{"class":3136,"line":4406},[3138,6958,3868],{"class":3836},[3138,6960,4398],{"class":3855},[3138,6962,6963,6965],{"class":3136,"line":4414},[3138,6964,4526],{"class":3832},[3138,6966,3837],{"class":3836},[3138,6968,6969,6972],{"class":3136,"line":4424},[3138,6970,6971],{"class":3832},"      db",[3138,6973,3837],{"class":3836},[3138,6975,6976,6978,6980],{"class":3136,"line":4431},[3138,6977,5273],{"class":3832},[3138,6979,3852],{"class":3836},[3138,6981,5278],{"class":3855},[3138,6983,6984,6987,6989],{"class":3136,"line":4438},[3138,6985,6986],{"class":3832},"    restart",[3138,6988,3852],{"class":3836},[3138,6990,6991],{"class":3855},"on-failure\n",[3138,6993,6994],{"class":3136,"line":4445},[3138,6995,3772],{"emptyLinePlaceholder":3771},[3138,6997,6998,7000],{"class":3136,"line":4456},[3138,6999,4309],{"class":3832},[3138,7001,3837],{"class":3836},[3138,7003,7004,7006,7008],{"class":3136,"line":4466},[3138,7005,3849],{"class":3832},[3138,7007,3852],{"class":3836},[3138,7009,7010],{"class":3855},"postgres:16-alpine\n",[3138,7012,7013,7015],{"class":3136,"line":4476},[3138,7014,4325],{"class":3832},[3138,7016,3837],{"class":3836},[3138,7018,7019,7022,7024],{"class":3136,"line":4486},[3138,7020,7021],{"class":3832},"      POSTGRES_DB",[3138,7023,3852],{"class":3836},[3138,7025,7026],{"class":3855},"${POSTGRES_DB:-myapp}\n",[3138,7028,7029,7032,7034],{"class":3136,"line":4493},[3138,7030,7031],{"class":3832},"      POSTGRES_USER",[3138,7033,3852],{"class":3836},[3138,7035,7036],{"class":3855},"${POSTGRES_USER:-postgres}\n",[3138,7038,7039,7042,7044],{"class":3136,"line":4501},[3138,7040,7041],{"class":3832},"      POSTGRES_PASSWORD",[3138,7043,3852],{"class":3836},[3138,7045,7046],{"class":3855},"${POSTGRES_PASSWORD:-secret}\n",[3138,7048,7049,7051],{"class":3136,"line":4508},[3138,7050,4372],{"class":3832},[3138,7052,3837],{"class":3836},[3138,7054,7055,7057],{"class":3136,"line":4515},[3138,7056,3868],{"class":3836},[3138,7058,7059],{"class":3855},"pg-data:/var/lib/postgresql/data\n",[3138,7061,7062,7064],{"class":3136,"line":4523},[3138,7063,4388],{"class":3832},[3138,7065,3837],{"class":3836},[3138,7067,7068,7070],{"class":3136,"line":4531},[3138,7069,3868],{"class":3836},[3138,7071,4398],{"class":3855},[3138,7073,7074,7076],{"class":3136,"line":4539},[3138,7075,5328],{"class":3832},[3138,7077,3837],{"class":3836},[3138,7079,7080,7082,7084,7087,7089,7092],{"class":3136,"line":4544},[3138,7081,5335],{"class":3832},[3138,7083,5338],{"class":3836},[3138,7085,7086],{"class":3687},"\"CMD-SHELL\"",[3138,7088,4272],{"class":3836},[3138,7090,7091],{"class":3687},"\"pg_isready -U ${POSTGRES_USER:-postgres}\"",[3138,7093,5359],{"class":3836},[3138,7095,7096,7098,7100],{"class":3136,"line":4551},[3138,7097,5364],{"class":3832},[3138,7099,3852],{"class":3836},[3138,7101,5369],{"class":3855},[3138,7103,7104,7106,7108],{"class":3136,"line":4559},[3138,7105,5374],{"class":3832},[3138,7107,3852],{"class":3836},[3138,7109,5379],{"class":3855},[3138,7111,7112,7114,7116],{"class":3136,"line":4567},[3138,7113,5384],{"class":3832},[3138,7115,3852],{"class":3836},[3138,7117,5390],{"class":5389},[3138,7119,7120],{"class":3136,"line":4572},[3138,7121,3772],{"emptyLinePlaceholder":3771},[3138,7123,7124,7127],{"class":3136,"line":4579},[3138,7125,7126],{"class":3832},"  pgadmin",[3138,7128,3837],{"class":3836},[3138,7130,7131,7133,7135],{"class":3136,"line":4587},[3138,7132,3849],{"class":3832},[3138,7134,3852],{"class":3836},[3138,7136,7137],{"class":3855},"dpage/pgadmin4:latest\n",[3138,7139,7140,7142],{"class":3136,"line":5442},[3138,7141,3861],{"class":3832},[3138,7143,3837],{"class":3836},[3138,7145,7146,7148],{"class":3136,"line":5453},[3138,7147,3868],{"class":3836},[3138,7149,7150],{"class":3687},"\"5050:80\"\n",[3138,7152,7153,7155],{"class":3136,"line":5463},[3138,7154,4325],{"class":3832},[3138,7156,3837],{"class":3836},[3138,7158,7159,7162,7164],{"class":3136,"line":5470},[3138,7160,7161],{"class":3832},"      PGADMIN_DEFAULT_EMAIL",[3138,7163,3852],{"class":3836},[3138,7165,7166],{"class":3855},"admin@admin.com\n",[3138,7168,7169,7172,7174],{"class":3136,"line":5477},[3138,7170,7171],{"class":3832},"      PGADMIN_DEFAULT_PASSWORD",[3138,7173,3852],{"class":3836},[3138,7175,5450],{"class":3855},[3138,7177,7178,7180],{"class":3136,"line":5484},[3138,7179,4372],{"class":3832},[3138,7181,3837],{"class":3836},[3138,7183,7184,7186],{"class":3136,"line":5491},[3138,7185,3868],{"class":3836},[3138,7187,7188],{"class":3855},"pgadmin-data:/var/lib/pgadmin\n",[3138,7190,7191,7193],{"class":3136,"line":5500},[3138,7192,4388],{"class":3832},[3138,7194,3837],{"class":3836},[3138,7196,7197,7199],{"class":3136,"line":5508},[3138,7198,3868],{"class":3836},[3138,7200,4398],{"class":3855},[3138,7202,7203,7205],{"class":3136,"line":5516},[3138,7204,4526],{"class":3832},[3138,7206,3837],{"class":3836},[3138,7208,7209,7211],{"class":3136,"line":5521},[3138,7210,3868],{"class":3836},[3138,7212,4536],{"class":3855},[3138,7214,7215,7217],{"class":3136,"line":5528},[3138,7216,5503],{"class":3832},[3138,7218,3837],{"class":3836},[3138,7220,7221,7223],{"class":3136,"line":5536},[3138,7222,3868],{"class":3836},[3138,7224,5513],{"class":3855},[3138,7226,7227],{"class":3136,"line":5541},[3138,7228,3772],{"emptyLinePlaceholder":3771},[3138,7230,7231,7233],{"class":3136,"line":5548},[3138,7232,4217],{"class":3832},[3138,7234,3837],{"class":3836},[3138,7236,7238,7241],{"class":3136,"line":7237},53,[3138,7239,7240],{"class":3832},"  pg-data",[3138,7242,3837],{"class":3836},[3138,7244,7246,7249],{"class":3136,"line":7245},54,[3138,7247,7248],{"class":3832},"  pgadmin-data",[3138,7250,3837],{"class":3836},[3138,7252,7254],{"class":3136,"line":7253},55,[3138,7255,3772],{"emptyLinePlaceholder":3771},[3138,7257,7259,7261],{"class":3136,"line":7258},56,[3138,7260,4213],{"class":3832},[3138,7262,3837],{"class":3836},[3138,7264,7266,7268],{"class":3136,"line":7265},57,[3138,7267,4590],{"class":3832},[3138,7269,3837],{"class":3836},[3670,7271,7275],{"className":7272,"code":7273,"filename":7274,"language":5712,"meta":3675,"style":3675},"language-env shiki shiki-themes light-plus dark-plus dark-plus","POSTGRES_DB=myapp\nPOSTGRES_USER=postgres\nPOSTGRES_PASSWORD=secret\n",".env",[3335,7276,7277,7282,7287],{"__ignoreMap":3675},[3138,7278,7279],{"class":3136,"line":3680},[3138,7280,7281],{},"POSTGRES_DB=myapp\n",[3138,7283,7284],{"class":3136,"line":3694},[3138,7285,7286],{},"POSTGRES_USER=postgres\n",[3138,7288,7289],{"class":3136,"line":3755},[3138,7290,7291],{},"POSTGRES_PASSWORD=secret\n",[3114,7293,7294],{},[3120,7295,5582,7296,3820],{},[3335,7297,5585],{},[4204,7299,7300,7312],{},[4207,7301,7303,7304,7307,7308,7311],{"name":7302,"type":5591},"ASPNETCORE_ENVIRONMENT","Визначає середовище виконання ASP.NET Core. У режимі ",[3335,7305,7306],{},"Development"," активується детальне відображення помилок, Swagger UI та інші діагностичні інструменти. У ",[3335,7309,7310],{},"Production"," ці функції вимкнені з міркувань безпеки.",[4207,7313,7315,7316,7319,7320,4673,7322,7325,7326,7328,7329,3394],{"name":7314,"type":5591},"ConnectionStrings__DefaultConnection","Рядок підключення до PostgreSQL у форматі Npgsql. Подвійне підкреслення ",[3335,7317,7318],{},"__"," є стандартним роздільником для ієрархічних конфігурацій ASP.NET Core через змінні середовища (замінює двокрапку ",[3335,7321,3820],{},[3335,7323,7324],{},"appsettings.json","). Значення підставляються зі змінних ",[3335,7327,7274],{}," через синтаксис ",[3335,7330,7331],{},"${VAR:-default}",[3114,7333,7334],{},[3120,7335,7336],{},"Ключові архітектурні рішення:",[7338,7339,7340,7354,7365],"card-group",{},[7341,7342,7345,7346,7349,7350,7353],"card",{"icon":7343,"title":7344},"i-heroicons-cube-transparent","Multi-stage Dockerfile","Образ ",[3335,7347,7348],{},"sdk"," (близько 800 МБ) використовується лише на етапі компіляції. Фінальний образ базується на ",[3335,7351,7352],{},"aspnet"," (близько 200 МБ) — він містить лише runtime, без компілятора та SDK-інструментів. Це зменшує attack surface та час завантаження образу.",[7341,7355,7358,7359,7361,7362,7364],{"icon":7356,"title":7357},"i-heroicons-arrow-path","Health Check + restart: on-failure","Поєднання двох механізмів стійкості: ",[3335,7360,6176],{}," гарантує, що PostgreSQL прийнятий запити до старту API; ",[3335,7363,6172],{}," підхоплює рідкісні race condition, якщо PostgreSQL ще ініціалізує схему, коли API вже намагається виконати міграції.",[7341,7366,7369,7370,7372,7373,7375],{"icon":7367,"title":7368},"i-heroicons-eye","pgAdmin через profiles","Адміністративний інтерфейс pgAdmin додається виключно через профіль ",[3335,7371,5104],{},". Це усуває як ризик безпеки (відкритий порт 5050 у production), так і зайве споживання ресурсів. У production-середовищі ",[3335,7374,4929],{}," не запустить pgAdmin взагалі.",[3114,7377,7378],{},[3120,7379,6028],{},[6030,7381,7382,7386,7405,7411,7414,7428,7443,7447,7463,7470,7474,7536,7540],{},[3423,7383,7385],{"id":7384},"підготувати-конфігурацію","Підготувати конфігурацію",[3670,7387,7389],{"className":3672,"code":7388,"language":3674,"meta":3675,"style":3675},"cp .env.example .env  # якщо є, або створити вручну\n",[3335,7390,7391],{"__ignoreMap":3675},[3138,7392,7393,7396,7399,7402],{"class":3136,"line":3680},[3138,7394,7395],{"class":3683},"cp",[3138,7397,7398],{"class":3687}," .env.example",[3138,7400,7401],{"class":3687}," .env",[3138,7403,7404],{"class":3697},"  # якщо є, або створити вручну\n",[3114,7406,7407,7408,7410],{},"Заповніть ",[3335,7409,7274],{}," реальними значеннями. Для локальної розробки можна використовувати дефолтні.",[3423,7412,6035],{"id":7413},"запустити-стек-1",[3670,7415,7416],{"className":3672,"code":6038,"language":3674,"meta":3675,"style":3675},[3335,7417,7418],{"__ignoreMap":3675},[3138,7419,7420,7422,7424,7426],{"class":3136,"line":3680},[3138,7421,3684],{"class":3683},[3138,7423,3688],{"class":3687},[3138,7425,4919],{"class":3687},[3138,7427,4922],{"class":3725},[3114,7429,7430,7431,7433,7434,7436,7437,7439,7440,7442],{},"Compose збере образ ",[3335,7432,5585],{}," з Dockerfile, запустить ",[3335,7435,4605],{}," з PostgreSQL, дочекається health check, а потім запустить ",[3335,7438,5585],{},". EF Core автоматично створить таблицю ",[3335,7441,6594],{}," при першому старті.",[3423,7444,7446],{"id":7445},"переглянути-логи-міграцій","Переглянути логи міграцій",[3670,7448,7450],{"className":3672,"code":7449,"language":3674,"meta":3675,"style":3675},"docker compose logs api\n",[3335,7451,7452],{"__ignoreMap":3675},[3138,7453,7454,7456,7458,7460],{"class":3136,"line":3680},[3138,7455,3684],{"class":3683},[3138,7457,3688],{"class":3687},[3138,7459,5048],{"class":3687},[3138,7461,7462],{"class":3687}," api\n",[3114,7464,7465,7466,7469],{},"У виводі мають бути рядки EF Core: ",[3335,7467,7468],{},"Applying migration '...'"," — підтвердження, що схема створена.",[3423,7471,7473],{"id":7472},"протестувати-api","Протестувати API",[3670,7475,7477],{"className":3672,"code":7476,"language":3674,"meta":3675,"style":3675},"curl http://localhost:5000/\n\ncurl -X POST http://localhost:5000/products \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"id\":0,\"name\":\"Laptop\",\"price\":999.99}'\n\ncurl http://localhost:5000/products\n",[3335,7478,7479,7486,7490,7507,7517,7525,7529],{"__ignoreMap":3675},[3138,7480,7481,7483],{"class":3136,"line":3680},[3138,7482,4011],{"class":3683},[3138,7484,7485],{"class":3687}," http://localhost:5000/\n",[3138,7487,7488],{"class":3136,"line":3694},[3138,7489,3772],{"emptyLinePlaceholder":3771},[3138,7491,7492,7494,7497,7500,7503],{"class":3136,"line":3755},[3138,7493,4011],{"class":3683},[3138,7495,7496],{"class":3725}," -X",[3138,7498,7499],{"class":3687}," POST",[3138,7501,7502],{"class":3687}," http://localhost:5000/products",[3138,7504,7506],{"class":7505},"sjcCO"," \\\n",[3138,7508,7509,7512,7515],{"class":3136,"line":3768},[3138,7510,7511],{"class":3725},"  -H",[3138,7513,7514],{"class":3687}," \"Content-Type: application/json\"",[3138,7516,7506],{"class":7505},[3138,7518,7519,7522],{"class":3136,"line":3775},[3138,7520,7521],{"class":3725},"  -d",[3138,7523,7524],{"class":3687}," '{\"id\":0,\"name\":\"Laptop\",\"price\":999.99}'\n",[3138,7526,7527],{"class":3136,"line":3781},[3138,7528,3772],{"emptyLinePlaceholder":3771},[3138,7530,7531,7533],{"class":3136,"line":4165},[3138,7532,4011],{"class":3683},[3138,7534,7535],{"class":3687}," http://localhost:5000/products\n",[3423,7537,7539],{"id":7538},"відкрити-pgadmin-опціонально","Відкрити pgAdmin (опціонально)",[3670,7541,7543],{"className":3672,"code":7542,"language":3674,"meta":3675,"style":3675},"docker compose --profile debug up -d pgadmin\nopen http://localhost:5050\n# admin@admin.com / admin\n",[3335,7544,7545,7562,7569],{"__ignoreMap":3675},[3138,7546,7547,7549,7551,7553,7555,7557,7559],{"class":3136,"line":3680},[3138,7548,3684],{"class":3683},[3138,7550,3688],{"class":3687},[3138,7552,6107],{"class":3725},[3138,7554,6110],{"class":3687},[3138,7556,4919],{"class":3687},[3138,7558,6115],{"class":3725},[3138,7560,7561],{"class":3687}," pgadmin\n",[3138,7563,7564,7566],{"class":3136,"line":3694},[3138,7565,6123],{"class":3683},[3138,7567,7568],{"class":3687}," http://localhost:5050\n",[3138,7570,7571],{"class":3136,"line":3755},[3138,7572,7573],{"class":3697},"# admin@admin.com / admin\n",[3641,7575,7576,7579,7580,4673,7583,7586],{},[3120,7577,7578],{},"EF Core міграції у production:"," Виклик ",[3335,7581,7582],{},"db.Database.Migrate()",[3335,7584,7585],{},"Program.cs"," зручний для development, але у production рекомендується виносити міграції в окремий ініціалізаційний крок або Kubernetes Job. Це дозволяє відкатити міграцію перед стартом нової версії застосунку.",[3416,7588],{},[3423,7590,7592],{"id":7591},"приклад-nextjs-fullstack-postgresql-redis","Приклад: Next.js (Fullstack) + PostgreSQL + Redis",[3114,7594,7595,7596,7599],{},"Next.js у режимі fullstack-фреймворку поєднує server-side rendering, статичну генерацію та API Routes в одному процесі. Це дозволяє уникнути окремого бекенд-сервісу для відносно простих застосунків, де фронтенд і API мають спільну кодову базу та типи. PostgreSQL забезпечує надійне реляційне зберігання через Prisma ORM, тоді як Redis відіграє роль кешу за патерном ",[3120,7597,7598],{},"Cache-Aside"," (Lazy Loading): застосунок спочатку перевіряє кеш, і лише у разі промаху (cache miss) звертається до бази даних, зберігаючи результат у Redis для наступних запитів.",[3114,7601,7602,7603,7605,7606,7608,7609,7611,7612,3463,7615,7618],{},"З точки зору Compose-конфігурації цей стек демонструє важливу відмінність у ",[3335,7604,4790],{},": для ",[3335,7607,4605],{}," використовується ",[3335,7610,6176],{}," (PostgreSQL повинен бути готовим до прийняття підключень), тоді як для ",[3335,7613,7614],{},"cache",[3335,7616,7617],{},"condition: service_started"," (Redis стартує майже миттєво і не потребує складного health check). Ця асиметрія відображає реальну різницю у часі ініціалізації сервісів.",[3114,7620,7621],{},[3120,7622,7623],{},"Архітектура запитів (Cache-Aside):",[7625,7626,7627],"mermaid",{},[3670,7628,7631],{"className":7629,"code":7630,"language":7625,"meta":3675,"style":3675},"language-mermaid shiki shiki-themes light-plus dark-plus dark-plus","graph LR\n    A[\"Браузер\"] --> B[\"Next.js\u003Cbr/>API Route\"]\n    B --> C{\"Redis\u003Cbr/>Cache\"}\n    C -- \"HIT\u003Cbr/>X-Cache: HIT\" --> A\n    C -- \"MISS\" --> D[\"PostgreSQL\"]\n    D --> E[\"Зберегти\u003Cbr/>у Redis (60s)\"]\n    E -- \"X-Cache: MISS\" --> A\n    style A fill:#3b82f6,stroke:#1d4ed8,color:#ffffff\n    style B fill:#64748b,stroke:#334155,color:#ffffff\n    style C fill:#f59e0b,stroke:#b45309,color:#ffffff\n    style D fill:#64748b,stroke:#334155,color:#ffffff\n    style E fill:#64748b,stroke:#334155,color:#ffffff\n",[3335,7632,7633,7638,7643,7648,7653,7658,7663,7668,7673,7678,7683,7688],{"__ignoreMap":3675},[3138,7634,7635],{"class":3136,"line":3680},[3138,7636,7637],{},"graph LR\n",[3138,7639,7640],{"class":3136,"line":3694},[3138,7641,7642],{},"    A[\"Браузер\"] --> B[\"Next.js\u003Cbr/>API Route\"]\n",[3138,7644,7645],{"class":3136,"line":3755},[3138,7646,7647],{},"    B --> C{\"Redis\u003Cbr/>Cache\"}\n",[3138,7649,7650],{"class":3136,"line":3768},[3138,7651,7652],{},"    C -- \"HIT\u003Cbr/>X-Cache: HIT\" --> A\n",[3138,7654,7655],{"class":3136,"line":3775},[3138,7656,7657],{},"    C -- \"MISS\" --> D[\"PostgreSQL\"]\n",[3138,7659,7660],{"class":3136,"line":3781},[3138,7661,7662],{},"    D --> E[\"Зберегти\u003Cbr/>у Redis (60s)\"]\n",[3138,7664,7665],{"class":3136,"line":4165},[3138,7666,7667],{},"    E -- \"X-Cache: MISS\" --> A\n",[3138,7669,7670],{"class":3136,"line":4178},[3138,7671,7672],{},"    style A fill:#3b82f6,stroke:#1d4ed8,color:#ffffff\n",[3138,7674,7675],{"class":3136,"line":4189},[3138,7676,7677],{},"    style B fill:#64748b,stroke:#334155,color:#ffffff\n",[3138,7679,7680],{"class":3136,"line":4377},[3138,7681,7682],{},"    style C fill:#f59e0b,stroke:#b45309,color:#ffffff\n",[3138,7684,7685],{"class":3136,"line":4385},[3138,7686,7687],{},"    style D fill:#64748b,stroke:#334155,color:#ffffff\n",[3138,7689,7690],{"class":3136,"line":4393},[3138,7691,7692],{},"    style E fill:#64748b,stroke:#334155,color:#ffffff\n",[3114,7694,7695],{},[3120,7696,5122],{},[6186,7698,7699,8172,8347],{},[3670,7700,7702],{"className":3823,"code":7701,"filename":3409,"language":3825,"meta":3675,"style":3675},"services:\n  web:\n    build:\n      context: ./web\n      dockerfile: Dockerfile\n      target: development\n    ports:\n      - \"3000:3000\"\n    volumes:\n      - ./web:/app\n      - /app/node_modules\n      - /app/.next\n    environment:\n      DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-secret}@db:5432/${POSTGRES_DB:-nextapp}\n      REDIS_URL: redis://cache:6379\n      NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-dev-secret-change-in-prod}\n      NEXTAUTH_URL: http://localhost:3000\n    networks:\n      - app-net\n    depends_on:\n      db:\n        condition: service_healthy\n      cache:\n        condition: service_started\n\n  db:\n    image: postgres:16-alpine\n    environment:\n      POSTGRES_DB: ${POSTGRES_DB:-nextapp}\n      POSTGRES_USER: ${POSTGRES_USER:-postgres}\n      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-secret}\n    volumes:\n      - pg-data:/var/lib/postgresql/data\n    networks:\n      - app-net\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-postgres}\"]\n      interval: 10s\n      timeout: 5s\n      retries: 5\n\n  cache:\n    image: redis:7-alpine\n    command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru\n    volumes:\n      - redis-data:/data\n    networks:\n      - app-net\n\n  redis-insight:\n    image: redislabs/redisinsight:latest\n    ports:\n      - \"5540:5540\"\n    networks:\n      - app-net\n    depends_on:\n      - cache\n    profiles:\n      - debug\n\nvolumes:\n  pg-data:\n  redis-data:\n\nnetworks:\n  app-net:\n",[3335,7703,7704,7710,7716,7722,7731,7739,7748,7754,7760,7766,7773,7780,7787,7793,7803,7813,7823,7833,7839,7846,7852,7858,7866,7873,7882,7886,7892,7900,7906,7915,7923,7931,7937,7943,7949,7955,7961,7975,7983,7991,7999,8003,8010,8019,8028,8034,8041,8047,8053,8057,8064,8073,8079,8086,8092,8098,8104,8111,8118,8125,8130,8137,8144,8152,8157,8164],{"__ignoreMap":3675},[3138,7705,7706,7708],{"class":3136,"line":3680},[3138,7707,3833],{"class":3832},[3138,7709,3837],{"class":3836},[3138,7711,7712,7714],{"class":3136,"line":3694},[3138,7713,3842],{"class":3832},[3138,7715,3837],{"class":3836},[3138,7717,7718,7720],{"class":3136,"line":3755},[3138,7719,6874],{"class":3832},[3138,7721,3837],{"class":3836},[3138,7723,7724,7726,7728],{"class":3136,"line":3768},[3138,7725,6881],{"class":3832},[3138,7727,3852],{"class":3836},[3138,7729,7730],{"class":3855},"./web\n",[3138,7732,7733,7735,7737],{"class":3136,"line":3775},[3138,7734,6891],{"class":3832},[3138,7736,3852],{"class":3836},[3138,7738,6896],{"class":3855},[3138,7740,7741,7744,7746],{"class":3136,"line":3781},[3138,7742,7743],{"class":3832},"      target",[3138,7745,3852],{"class":3836},[3138,7747,5243],{"class":3855},[3138,7749,7750,7752],{"class":3136,"line":4165},[3138,7751,3861],{"class":3832},[3138,7753,3837],{"class":3836},[3138,7755,7756,7758],{"class":3136,"line":4178},[3138,7757,3868],{"class":3836},[3138,7759,5217],{"class":3687},[3138,7761,7762,7764],{"class":3136,"line":4189},[3138,7763,4372],{"class":3832},[3138,7765,3837],{"class":3836},[3138,7767,7768,7770],{"class":3136,"line":4377},[3138,7769,3868],{"class":3836},[3138,7771,7772],{"class":3855},"./web:/app\n",[3138,7774,7775,7777],{"class":3136,"line":4385},[3138,7776,3868],{"class":3836},[3138,7778,7779],{"class":3855},"/app/node_modules\n",[3138,7781,7782,7784],{"class":3136,"line":4393},[3138,7783,3868],{"class":3836},[3138,7785,7786],{"class":3855},"/app/.next\n",[3138,7788,7789,7791],{"class":3136,"line":4401},[3138,7790,4325],{"class":3832},[3138,7792,3837],{"class":3836},[3138,7794,7795,7798,7800],{"class":3136,"line":4406},[3138,7796,7797],{"class":3832},"      DATABASE_URL",[3138,7799,3852],{"class":3836},[3138,7801,7802],{"class":3855},"postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-secret}@db:5432/${POSTGRES_DB:-nextapp}\n",[3138,7804,7805,7808,7810],{"class":3136,"line":4414},[3138,7806,7807],{"class":3832},"      REDIS_URL",[3138,7809,3852],{"class":3836},[3138,7811,7812],{"class":3855},"redis://cache:6379\n",[3138,7814,7815,7818,7820],{"class":3136,"line":4424},[3138,7816,7817],{"class":3832},"      NEXTAUTH_SECRET",[3138,7819,3852],{"class":3836},[3138,7821,7822],{"class":3855},"${NEXTAUTH_SECRET:-dev-secret-change-in-prod}\n",[3138,7824,7825,7828,7830],{"class":3136,"line":4431},[3138,7826,7827],{"class":3832},"      NEXTAUTH_URL",[3138,7829,3852],{"class":3836},[3138,7831,7832],{"class":3855},"http://localhost:3000\n",[3138,7834,7835,7837],{"class":3136,"line":4438},[3138,7836,4388],{"class":3832},[3138,7838,3837],{"class":3836},[3138,7840,7841,7843],{"class":3136,"line":4445},[3138,7842,3868],{"class":3836},[3138,7844,7845],{"class":3855},"app-net\n",[3138,7847,7848,7850],{"class":3136,"line":4456},[3138,7849,4526],{"class":3832},[3138,7851,3837],{"class":3836},[3138,7853,7854,7856],{"class":3136,"line":4466},[3138,7855,6971],{"class":3832},[3138,7857,3837],{"class":3836},[3138,7859,7860,7862,7864],{"class":3136,"line":4476},[3138,7861,5273],{"class":3832},[3138,7863,3852],{"class":3836},[3138,7865,5278],{"class":3855},[3138,7867,7868,7871],{"class":3136,"line":4486},[3138,7869,7870],{"class":3832},"      cache",[3138,7872,3837],{"class":3836},[3138,7874,7875,7877,7879],{"class":3136,"line":4493},[3138,7876,5273],{"class":3832},[3138,7878,3852],{"class":3836},[3138,7880,7881],{"class":3855},"service_started\n",[3138,7883,7884],{"class":3136,"line":4501},[3138,7885,3772],{"emptyLinePlaceholder":3771},[3138,7887,7888,7890],{"class":3136,"line":4508},[3138,7889,4309],{"class":3832},[3138,7891,3837],{"class":3836},[3138,7893,7894,7896,7898],{"class":3136,"line":4515},[3138,7895,3849],{"class":3832},[3138,7897,3852],{"class":3836},[3138,7899,7010],{"class":3855},[3138,7901,7902,7904],{"class":3136,"line":4523},[3138,7903,4325],{"class":3832},[3138,7905,3837],{"class":3836},[3138,7907,7908,7910,7912],{"class":3136,"line":4531},[3138,7909,7021],{"class":3832},[3138,7911,3852],{"class":3836},[3138,7913,7914],{"class":3855},"${POSTGRES_DB:-nextapp}\n",[3138,7916,7917,7919,7921],{"class":3136,"line":4539},[3138,7918,7031],{"class":3832},[3138,7920,3852],{"class":3836},[3138,7922,7036],{"class":3855},[3138,7924,7925,7927,7929],{"class":3136,"line":4544},[3138,7926,7041],{"class":3832},[3138,7928,3852],{"class":3836},[3138,7930,7046],{"class":3855},[3138,7932,7933,7935],{"class":3136,"line":4551},[3138,7934,4372],{"class":3832},[3138,7936,3837],{"class":3836},[3138,7938,7939,7941],{"class":3136,"line":4559},[3138,7940,3868],{"class":3836},[3138,7942,7059],{"class":3855},[3138,7944,7945,7947],{"class":3136,"line":4567},[3138,7946,4388],{"class":3832},[3138,7948,3837],{"class":3836},[3138,7950,7951,7953],{"class":3136,"line":4572},[3138,7952,3868],{"class":3836},[3138,7954,7845],{"class":3855},[3138,7956,7957,7959],{"class":3136,"line":4579},[3138,7958,5328],{"class":3832},[3138,7960,3837],{"class":3836},[3138,7962,7963,7965,7967,7969,7971,7973],{"class":3136,"line":4587},[3138,7964,5335],{"class":3832},[3138,7966,5338],{"class":3836},[3138,7968,7086],{"class":3687},[3138,7970,4272],{"class":3836},[3138,7972,7091],{"class":3687},[3138,7974,5359],{"class":3836},[3138,7976,7977,7979,7981],{"class":3136,"line":5442},[3138,7978,5364],{"class":3832},[3138,7980,3852],{"class":3836},[3138,7982,5369],{"class":3855},[3138,7984,7985,7987,7989],{"class":3136,"line":5453},[3138,7986,5374],{"class":3832},[3138,7988,3852],{"class":3836},[3138,7990,5379],{"class":3855},[3138,7992,7993,7995,7997],{"class":3136,"line":5463},[3138,7994,5384],{"class":3832},[3138,7996,3852],{"class":3836},[3138,7998,5390],{"class":5389},[3138,8000,8001],{"class":3136,"line":5470},[3138,8002,3772],{"emptyLinePlaceholder":3771},[3138,8004,8005,8008],{"class":3136,"line":5477},[3138,8006,8007],{"class":3832},"  cache",[3138,8009,3837],{"class":3836},[3138,8011,8012,8014,8016],{"class":3136,"line":5484},[3138,8013,3849],{"class":3832},[3138,8015,3852],{"class":3836},[3138,8017,8018],{"class":3855},"redis:7-alpine\n",[3138,8020,8021,8023,8025],{"class":3136,"line":5491},[3138,8022,5199],{"class":3832},[3138,8024,3852],{"class":3836},[3138,8026,8027],{"class":3855},"redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru\n",[3138,8029,8030,8032],{"class":3136,"line":5500},[3138,8031,4372],{"class":3832},[3138,8033,3837],{"class":3836},[3138,8035,8036,8038],{"class":3136,"line":5508},[3138,8037,3868],{"class":3836},[3138,8039,8040],{"class":3855},"redis-data:/data\n",[3138,8042,8043,8045],{"class":3136,"line":5516},[3138,8044,4388],{"class":3832},[3138,8046,3837],{"class":3836},[3138,8048,8049,8051],{"class":3136,"line":5521},[3138,8050,3868],{"class":3836},[3138,8052,7845],{"class":3855},[3138,8054,8055],{"class":3136,"line":5528},[3138,8056,3772],{"emptyLinePlaceholder":3771},[3138,8058,8059,8062],{"class":3136,"line":5536},[3138,8060,8061],{"class":3832},"  redis-insight",[3138,8063,3837],{"class":3836},[3138,8065,8066,8068,8070],{"class":3136,"line":5541},[3138,8067,3849],{"class":3832},[3138,8069,3852],{"class":3836},[3138,8071,8072],{"class":3855},"redislabs/redisinsight:latest\n",[3138,8074,8075,8077],{"class":3136,"line":5548},[3138,8076,3861],{"class":3832},[3138,8078,3837],{"class":3836},[3138,8080,8081,8083],{"class":3136,"line":7237},[3138,8082,3868],{"class":3836},[3138,8084,8085],{"class":3687},"\"5540:5540\"\n",[3138,8087,8088,8090],{"class":3136,"line":7245},[3138,8089,4388],{"class":3832},[3138,8091,3837],{"class":3836},[3138,8093,8094,8096],{"class":3136,"line":7253},[3138,8095,3868],{"class":3836},[3138,8097,7845],{"class":3855},[3138,8099,8100,8102],{"class":3136,"line":7258},[3138,8101,4526],{"class":3832},[3138,8103,3837],{"class":3836},[3138,8105,8106,8108],{"class":3136,"line":7265},[3138,8107,3868],{"class":3836},[3138,8109,8110],{"class":3855},"cache\n",[3138,8112,8114,8116],{"class":3136,"line":8113},58,[3138,8115,5503],{"class":3832},[3138,8117,3837],{"class":3836},[3138,8119,8121,8123],{"class":3136,"line":8120},59,[3138,8122,3868],{"class":3836},[3138,8124,5513],{"class":3855},[3138,8126,8128],{"class":3136,"line":8127},60,[3138,8129,3772],{"emptyLinePlaceholder":3771},[3138,8131,8133,8135],{"class":3136,"line":8132},61,[3138,8134,4217],{"class":3832},[3138,8136,3837],{"class":3836},[3138,8138,8140,8142],{"class":3136,"line":8139},62,[3138,8141,7240],{"class":3832},[3138,8143,3837],{"class":3836},[3138,8145,8147,8150],{"class":3136,"line":8146},63,[3138,8148,8149],{"class":3832},"  redis-data",[3138,8151,3837],{"class":3836},[3138,8153,8155],{"class":3136,"line":8154},64,[3138,8156,3772],{"emptyLinePlaceholder":3771},[3138,8158,8160,8162],{"class":3136,"line":8159},65,[3138,8161,4213],{"class":3832},[3138,8163,3837],{"class":3836},[3138,8165,8167,8170],{"class":3136,"line":8166},66,[3138,8168,8169],{"class":3832},"  app-net",[3138,8171,3837],{"class":3836},[3670,8173,8176],{"className":6190,"code":8174,"filename":8175,"language":6193,"meta":3675,"style":3675},"FROM node:20-alpine AS base\nWORKDIR /app\nCOPY package*.json .\n\nFROM base AS development\nRUN npm install\nCOPY . .\nCMD [\"npm\", \"run\", \"dev\"]\n\nFROM base AS builder\nRUN npm ci\nCOPY . .\nRUN npm run build\n\nFROM node:20-alpine AS production\nWORKDIR /app\nCOPY --from=builder /app/.next/standalone .\nCOPY --from=builder /app/.next/static ./.next/static\nCOPY --from=builder /app/public ./public\nCMD [\"node\", \"server.js\"]\n","web/Dockerfile",[3335,8177,8178,8190,8196,8203,8207,8219,8226,8232,8254,8258,8269,8276,8282,8289,8293,8304,8310,8317,8324,8331],{"__ignoreMap":3675},[3138,8179,8180,8182,8185,8187],{"class":3136,"line":3680},[3138,8181,6200],{"class":3725},[3138,8183,8184],{"class":3836}," node:20-alpine ",[3138,8186,6206],{"class":3725},[3138,8188,8189],{"class":3836}," base\n",[3138,8191,8192,8194],{"class":3136,"line":3694},[3138,8193,6214],{"class":3725},[3138,8195,6265],{"class":3836},[3138,8197,8198,8200],{"class":3136,"line":3755},[3138,8199,6222],{"class":3725},[3138,8201,8202],{"class":3836}," package*.json .\n",[3138,8204,8205],{"class":3136,"line":3768},[3138,8206,3772],{"emptyLinePlaceholder":3771},[3138,8208,8209,8211,8214,8216],{"class":3136,"line":3775},[3138,8210,6200],{"class":3725},[3138,8212,8213],{"class":3836}," base ",[3138,8215,6206],{"class":3725},[3138,8217,8218],{"class":3836}," development\n",[3138,8220,8221,8223],{"class":3136,"line":3781},[3138,8222,6230],{"class":3725},[3138,8224,8225],{"class":3836}," npm install\n",[3138,8227,8228,8230],{"class":3136,"line":4165},[3138,8229,6222],{"class":3725},[3138,8231,6240],{"class":3836},[3138,8233,8234,8237,8239,8242,8244,8247,8249,8252],{"class":3136,"line":4178},[3138,8235,8236],{"class":3725},"CMD",[3138,8238,6280],{"class":3836},[3138,8240,8241],{"class":3687},"\"npm\"",[3138,8243,4272],{"class":3836},[3138,8245,8246],{"class":3687},"\"run\"",[3138,8248,4272],{"class":3836},[3138,8250,8251],{"class":3687},"\"dev\"",[3138,8253,5359],{"class":3836},[3138,8255,8256],{"class":3136,"line":4189},[3138,8257,3772],{"emptyLinePlaceholder":3771},[3138,8259,8260,8262,8264,8266],{"class":3136,"line":4377},[3138,8261,6200],{"class":3725},[3138,8263,8213],{"class":3836},[3138,8265,6206],{"class":3725},[3138,8267,8268],{"class":3836}," builder\n",[3138,8270,8271,8273],{"class":3136,"line":4385},[3138,8272,6230],{"class":3725},[3138,8274,8275],{"class":3836}," npm ci\n",[3138,8277,8278,8280],{"class":3136,"line":4393},[3138,8279,6222],{"class":3725},[3138,8281,6240],{"class":3836},[3138,8283,8284,8286],{"class":3136,"line":4401},[3138,8285,6230],{"class":3725},[3138,8287,8288],{"class":3836}," npm run build\n",[3138,8290,8291],{"class":3136,"line":4406},[3138,8292,3772],{"emptyLinePlaceholder":3771},[3138,8294,8295,8297,8299,8301],{"class":3136,"line":4414},[3138,8296,6200],{"class":3725},[3138,8298,8184],{"class":3836},[3138,8300,6206],{"class":3725},[3138,8302,8303],{"class":3836}," production\n",[3138,8305,8306,8308],{"class":3136,"line":4424},[3138,8307,6214],{"class":3725},[3138,8309,6265],{"class":3836},[3138,8311,8312,8314],{"class":3136,"line":4431},[3138,8313,6222],{"class":3725},[3138,8315,8316],{"class":3836}," --from=builder /app/.next/standalone .\n",[3138,8318,8319,8321],{"class":3136,"line":4438},[3138,8320,6222],{"class":3725},[3138,8322,8323],{"class":3836}," --from=builder /app/.next/static ./.next/static\n",[3138,8325,8326,8328],{"class":3136,"line":4445},[3138,8327,6222],{"class":3725},[3138,8329,8330],{"class":3836}," --from=builder /app/public ./public\n",[3138,8332,8333,8335,8337,8340,8342,8345],{"class":3136,"line":4456},[3138,8334,8236],{"class":3725},[3138,8336,6280],{"class":3836},[3138,8338,8339],{"class":3687},"\"node\"",[3138,8341,4272],{"class":3836},[3138,8343,8344],{"class":3687},"\"server.js\"",[3138,8346,5359],{"class":3836},[3670,8348,8353],{"className":8349,"code":8350,"filename":8351,"language":8352,"meta":3675,"style":3675},"language-typescript shiki shiki-themes light-plus dark-plus dark-plus","import { NextResponse } from 'next/server'\nimport { createClient } from 'redis'\nimport { PrismaClient } from '@prisma/client'\n\nconst prisma = new PrismaClient()\nconst redis = createClient({ url: process.env.REDIS_URL })\nawait redis.connect()\n\nexport async function GET() {\n  const cacheKey = 'posts:all'\n\n  // Cache-Aside: спочатку перевіряємо кеш\n  const cached = await redis.get(cacheKey)\n  if (cached) {\n    return NextResponse.json(JSON.parse(cached), {\n      headers: { 'X-Cache': 'HIT' },\n    })\n  }\n\n  // Cache miss: читаємо з PostgreSQL\n  const posts = await prisma.post.findMany({\n    orderBy: { createdAt: 'desc' },\n    take: 20,\n  })\n\n  // Записуємо в кеш на 60 секунд\n  await redis.setEx(cacheKey, 60, JSON.stringify(posts))\n\n  return NextResponse.json(posts, {\n    headers: { 'X-Cache': 'MISS' },\n  })\n}\n","web/app/api/posts/route.ts","typescript",[3335,8354,8355,8371,8387,8403,8407,8423,8453,8465,8469,8486,8498,8502,8507,8531,8544,8572,8590,8595,8599,8603,8608,8634,8649,8659,8664,8668,8673,8710,8714,8731,8747,8751],{"__ignoreMap":3675},[3138,8356,8357,8359,8361,8364,8366,8368],{"class":3136,"line":3680},[3138,8358,5626],{"class":5625},[3138,8360,5643],{"class":3836},[3138,8362,8363],{"class":5629},"NextResponse",[3138,8365,5649],{"class":3836},[3138,8367,5652],{"class":5625},[3138,8369,8370],{"class":3687}," 'next/server'\n",[3138,8372,8373,8375,8377,8380,8382,8384],{"class":3136,"line":3694},[3138,8374,5626],{"class":5625},[3138,8376,5643],{"class":3836},[3138,8378,8379],{"class":5629},"createClient",[3138,8381,5649],{"class":3836},[3138,8383,5652],{"class":5625},[3138,8385,8386],{"class":3687}," 'redis'\n",[3138,8388,8389,8391,8393,8396,8398,8400],{"class":3136,"line":3755},[3138,8390,5626],{"class":5625},[3138,8392,5643],{"class":3836},[3138,8394,8395],{"class":5629},"PrismaClient",[3138,8397,5649],{"class":3836},[3138,8399,5652],{"class":5625},[3138,8401,8402],{"class":3687}," '@prisma/client'\n",[3138,8404,8405],{"class":3136,"line":3768},[3138,8406,3772],{"emptyLinePlaceholder":3771},[3138,8408,8409,8411,8414,8416,8418,8421],{"class":3136,"line":3775},[3138,8410,5664],{"class":3725},[3138,8412,8413],{"class":5667}," prisma",[3138,8415,5671],{"class":3836},[3138,8417,5698],{"class":3725},[3138,8419,8420],{"class":3683}," PrismaClient",[3138,8422,5738],{"class":3836},[3138,8424,8425,8427,8430,8432,8434,8436,8439,8442,8444,8446,8448,8451],{"class":3136,"line":3781},[3138,8426,5664],{"class":3725},[3138,8428,8429],{"class":5667}," redis",[3138,8431,5671],{"class":3836},[3138,8433,8379],{"class":3683},[3138,8435,5677],{"class":3836},[3138,8437,8438],{"class":5629},"url:",[3138,8440,8441],{"class":5629}," process",[3138,8443,3394],{"class":3836},[3138,8445,5712],{"class":5629},[3138,8447,3394],{"class":3836},[3138,8449,8450],{"class":5667},"REDIS_URL",[3138,8452,5686],{"class":3836},[3138,8454,8455,8457,8459,8461,8463],{"class":3136,"line":4165},[3138,8456,5728],{"class":5625},[3138,8458,8429],{"class":5629},[3138,8460,3394],{"class":3836},[3138,8462,5735],{"class":3683},[3138,8464,5738],{"class":3836},[3138,8466,8467],{"class":3136,"line":4178},[3138,8468,3772],{"emptyLinePlaceholder":3771},[3138,8470,8471,8474,8477,8480,8483],{"class":3136,"line":4189},[3138,8472,8473],{"class":5625},"export",[3138,8475,8476],{"class":3725}," async",[3138,8478,8479],{"class":3725}," function",[3138,8481,8482],{"class":3683}," GET",[3138,8484,8485],{"class":3836},"() {\n",[3138,8487,8488,8490,8493,8495],{"class":3136,"line":4377},[3138,8489,5794],{"class":3725},[3138,8491,8492],{"class":5667}," cacheKey",[3138,8494,5671],{"class":3836},[3138,8496,8497],{"class":3687},"'posts:all'\n",[3138,8499,8500],{"class":3136,"line":4385},[3138,8501,3772],{"emptyLinePlaceholder":3771},[3138,8503,8504],{"class":3136,"line":4393},[3138,8505,8506],{"class":3697},"  // Cache-Aside: спочатку перевіряємо кеш\n",[3138,8508,8509,8511,8514,8516,8518,8520,8522,8524,8526,8529],{"class":3136,"line":4401},[3138,8510,5794],{"class":3725},[3138,8512,8513],{"class":5667}," cached",[3138,8515,5671],{"class":3836},[3138,8517,5728],{"class":5625},[3138,8519,8429],{"class":5629},[3138,8521,3394],{"class":3836},[3138,8523,5770],{"class":3683},[3138,8525,5704],{"class":3836},[3138,8527,8528],{"class":5629},"cacheKey",[3138,8530,5719],{"class":3836},[3138,8532,8533,8536,8538,8541],{"class":3136,"line":4406},[3138,8534,8535],{"class":5625},"  if",[3138,8537,6450],{"class":3836},[3138,8539,8540],{"class":5629},"cached",[3138,8542,8543],{"class":3836},") {\n",[3138,8545,8546,8548,8551,8553,8555,8557,8560,8562,8565,8567,8569],{"class":3136,"line":4414},[3138,8547,6681],{"class":5625},[3138,8549,8550],{"class":5629}," NextResponse",[3138,8552,3394],{"class":3836},[3138,8554,5950],{"class":3683},[3138,8556,5704],{"class":3836},[3138,8558,8559],{"class":5667},"JSON",[3138,8561,3394],{"class":3836},[3138,8563,8564],{"class":3683},"parse",[3138,8566,5704],{"class":3836},[3138,8568,8540],{"class":5629},[3138,8570,8571],{"class":3836},"), {\n",[3138,8573,8574,8577,8579,8582,8584,8587],{"class":3136,"line":4424},[3138,8575,8576],{"class":5629},"      headers:",[3138,8578,5643],{"class":3836},[3138,8580,8581],{"class":3687},"'X-Cache'",[3138,8583,3820],{"class":5629},[3138,8585,8586],{"class":3687}," 'HIT'",[3138,8588,8589],{"class":3836}," },\n",[3138,8591,8592],{"class":3136,"line":4431},[3138,8593,8594],{"class":3836},"    })\n",[3138,8596,8597],{"class":3136,"line":4438},[3138,8598,6018],{"class":3836},[3138,8600,8601],{"class":3136,"line":4445},[3138,8602,3772],{"emptyLinePlaceholder":3771},[3138,8604,8605],{"class":3136,"line":4456},[3138,8606,8607],{"class":3697},"  // Cache miss: читаємо з PostgreSQL\n",[3138,8609,8610,8612,8615,8617,8619,8621,8623,8626,8628,8631],{"class":3136,"line":4466},[3138,8611,5794],{"class":3725},[3138,8613,8614],{"class":5667}," posts",[3138,8616,5671],{"class":3836},[3138,8618,5728],{"class":5625},[3138,8620,8413],{"class":5629},[3138,8622,3394],{"class":3836},[3138,8624,8625],{"class":5629},"post",[3138,8627,3394],{"class":3836},[3138,8629,8630],{"class":3683},"findMany",[3138,8632,8633],{"class":3836},"({\n",[3138,8635,8636,8639,8641,8644,8647],{"class":3136,"line":4476},[3138,8637,8638],{"class":5629},"    orderBy:",[3138,8640,5643],{"class":3836},[3138,8642,8643],{"class":5629},"createdAt:",[3138,8645,8646],{"class":3687}," 'desc'",[3138,8648,8589],{"class":3836},[3138,8650,8651,8654,8657],{"class":3136,"line":4486},[3138,8652,8653],{"class":5629},"    take:",[3138,8655,8656],{"class":5389}," 20",[3138,8658,5971],{"class":3836},[3138,8660,8661],{"class":3136,"line":4493},[3138,8662,8663],{"class":3836},"  })\n",[3138,8665,8666],{"class":3136,"line":4501},[3138,8667,3772],{"emptyLinePlaceholder":3771},[3138,8669,8670],{"class":3136,"line":4508},[3138,8671,8672],{"class":3697},"  // Записуємо в кеш на 60 секунд\n",[3138,8674,8675,8677,8679,8681,8684,8686,8688,8690,8693,8695,8697,8699,8702,8704,8707],{"class":3136,"line":4515},[3138,8676,5826],{"class":5625},[3138,8678,8429],{"class":5629},[3138,8680,3394],{"class":3836},[3138,8682,8683],{"class":3683},"setEx",[3138,8685,5704],{"class":3836},[3138,8687,8528],{"class":5629},[3138,8689,4272],{"class":3836},[3138,8691,8692],{"class":5389},"60",[3138,8694,4272],{"class":3836},[3138,8696,8559],{"class":5667},[3138,8698,3394],{"class":3836},[3138,8700,8701],{"class":3683},"stringify",[3138,8703,5704],{"class":3836},[3138,8705,8706],{"class":5629},"posts",[3138,8708,8709],{"class":3836},"))\n",[3138,8711,8712],{"class":3136,"line":4523},[3138,8713,3772],{"emptyLinePlaceholder":3771},[3138,8715,8716,8718,8720,8722,8724,8726,8728],{"class":3136,"line":4531},[3138,8717,5860],{"class":5625},[3138,8719,8550],{"class":5629},[3138,8721,3394],{"class":3836},[3138,8723,5950],{"class":3683},[3138,8725,5704],{"class":3836},[3138,8727,8706],{"class":5629},[3138,8729,8730],{"class":3836},", {\n",[3138,8732,8733,8736,8738,8740,8742,8745],{"class":3136,"line":4539},[3138,8734,8735],{"class":5629},"    headers:",[3138,8737,5643],{"class":3836},[3138,8739,8581],{"class":3687},[3138,8741,3820],{"class":5629},[3138,8743,8744],{"class":3687}," 'MISS'",[3138,8746,8589],{"class":3836},[3138,8748,8749],{"class":3136,"line":4544},[3138,8750,8663],{"class":3836},[3138,8752,8753],{"class":3136,"line":4551},[3138,8754,6023],{"class":3836},[3114,8756,8757],{},[3120,8758,5582,8759,3820],{},[3335,8760,3976],{},[4204,8762,8763,8773,8779,8787],{},[4207,8764,8766,8767,8769,8770,3394],{"name":8765,"type":5591},"DATABASE_URL","Рядок підключення Prisma до PostgreSQL. Використовує назву сервісу ",[3335,8768,4605],{}," як hostname. Формат: ",[3335,8771,8772],{},"postgresql://user:password@host:port/database",[4207,8774,8775,8776,8778],{"name":8450,"type":5591},"Адреса Redis-сервера. ",[3335,8777,7614],{}," — це назва сервісу у Compose, яка резолвиться Docker DNS. Порт 6379 є стандартним для Redis.",[4207,8780,8782,8783,8786],{"name":8781,"type":5591},"NEXTAUTH_SECRET","Секрет для підпису JWT-токенів NextAuth.js. У production обов'язково має бути замінений на криптографічно стійке значення (",[3335,8784,8785],{},"openssl rand -base64 32","). Значення за замовчуванням слугує лише для локальної розробки.",[4207,8788,8790],{"name":8789,"type":5591},"NEXTAUTH_URL","Базова URL-адреса застосунку. NextAuth.js використовує її для формування redirect URI після автентифікації. У production замінюється на реальний домен.",[3114,8792,8793],{},[3120,8794,8795],{},"Параметри Redis:",[3114,8797,8798,8799,8802],{},"Команда ",[3335,8800,8801],{},"redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru"," задає три важливі параметри:",[3325,8804,8805,8811,8817],{},[3328,8806,8807,8810],{},[3335,8808,8809],{},"--appendonly yes"," — вмикає AOF (Append-Only File) персистентність: кожна операція запису логується на диск, що дозволяє відновити кеш після перезапуску контейнера",[3328,8812,8813,8816],{},[3335,8814,8815],{},"--maxmemory 256mb"," — жорстке обмеження пам'яті, щоб Redis не з'їв увесь доступний RAM хоста",[3328,8818,8819,8822],{},[3335,8820,8821],{},"--maxmemory-policy allkeys-lru"," — при досягненні ліміту видаляти найменш нещодавно використані ключі (Least Recently Used). Найбільш підходяща політика для кешу",[3114,8824,8825],{},[3120,8826,6028],{},[8828,8829,8830,8950],"tabs",{},[8831,8832,8833],"tabs-item",{"label":7306},[3670,8834,8836],{"className":3672,"code":8835,"language":3674,"meta":3675,"style":3675},"# Запустити стек (Next.js у режимі hot-reload)\ndocker compose up -d\n\n# Переглянути логи\ndocker compose logs -f web\n\n# Перевірити Cache-Aside\ncurl -v http://localhost:3000/api/posts\n# X-Cache: MISS  ← перший запит, читається з PostgreSQL\n\ncurl -v http://localhost:3000/api/posts\n# X-Cache: HIT   ← з Redis кешу\n\n# Відкрити Redis Insight для інспекції ключів\ndocker compose --profile debug up -d redis-insight\nopen http://localhost:5540\n",[3335,8837,8838,8843,8853,8857,8862,8876,8880,8885,8895,8900,8904,8912,8917,8921,8926,8943],{"__ignoreMap":3675},[3138,8839,8840],{"class":3136,"line":3680},[3138,8841,8842],{"class":3697},"# Запустити стек (Next.js у режимі hot-reload)\n",[3138,8844,8845,8847,8849,8851],{"class":3136,"line":3694},[3138,8846,3684],{"class":3683},[3138,8848,3688],{"class":3687},[3138,8850,4919],{"class":3687},[3138,8852,4922],{"class":3725},[3138,8854,8855],{"class":3136,"line":3755},[3138,8856,3772],{"emptyLinePlaceholder":3771},[3138,8858,8859],{"class":3136,"line":3768},[3138,8860,8861],{"class":3697},"# Переглянути логи\n",[3138,8863,8864,8866,8868,8870,8873],{"class":3136,"line":3775},[3138,8865,3684],{"class":3683},[3138,8867,3688],{"class":3687},[3138,8869,5048],{"class":3687},[3138,8871,8872],{"class":3725}," -f",[3138,8874,8875],{"class":3687}," web\n",[3138,8877,8878],{"class":3136,"line":3781},[3138,8879,3772],{"emptyLinePlaceholder":3771},[3138,8881,8882],{"class":3136,"line":4165},[3138,8883,8884],{"class":3697},"# Перевірити Cache-Aside\n",[3138,8886,8887,8889,8892],{"class":3136,"line":4178},[3138,8888,4011],{"class":3683},[3138,8890,8891],{"class":3725}," -v",[3138,8893,8894],{"class":3687}," http://localhost:3000/api/posts\n",[3138,8896,8897],{"class":3136,"line":4189},[3138,8898,8899],{"class":3697},"# X-Cache: MISS  ← перший запит, читається з PostgreSQL\n",[3138,8901,8902],{"class":3136,"line":4377},[3138,8903,3772],{"emptyLinePlaceholder":3771},[3138,8905,8906,8908,8910],{"class":3136,"line":4385},[3138,8907,4011],{"class":3683},[3138,8909,8891],{"class":3725},[3138,8911,8894],{"class":3687},[3138,8913,8914],{"class":3136,"line":4393},[3138,8915,8916],{"class":3697},"# X-Cache: HIT   ← з Redis кешу\n",[3138,8918,8919],{"class":3136,"line":4401},[3138,8920,3772],{"emptyLinePlaceholder":3771},[3138,8922,8923],{"class":3136,"line":4406},[3138,8924,8925],{"class":3697},"# Відкрити Redis Insight для інспекції ключів\n",[3138,8927,8928,8930,8932,8934,8936,8938,8940],{"class":3136,"line":4414},[3138,8929,3684],{"class":3683},[3138,8931,3688],{"class":3687},[3138,8933,6107],{"class":3725},[3138,8935,6110],{"class":3687},[3138,8937,4919],{"class":3687},[3138,8939,6115],{"class":3725},[3138,8941,8942],{"class":3687}," redis-insight\n",[3138,8944,8945,8947],{"class":3136,"line":4424},[3138,8946,6123],{"class":3683},[3138,8948,8949],{"class":3687}," http://localhost:5540\n",[8831,8951,8952,9007],{"label":7310},[3670,8953,8955],{"className":3672,"code":8954,"language":3674,"meta":3675,"style":3675},"# Побудувати production-образ\ndocker compose -f docker-compose.yml -f docker-compose.prod.yml build\n\n# Запустити (Next.js у standalone-режимі)\ndocker compose -f docker-compose.yml -f docker-compose.prod.yml up -d\n",[3335,8956,8957,8962,8980,8984,8989],{"__ignoreMap":3675},[3138,8958,8959],{"class":3136,"line":3680},[3138,8960,8961],{"class":3697},"# Побудувати production-образ\n",[3138,8963,8964,8966,8968,8970,8973,8975,8978],{"class":3136,"line":3694},[3138,8965,3684],{"class":3683},[3138,8967,3688],{"class":3687},[3138,8969,8872],{"class":3725},[3138,8971,8972],{"class":3687}," docker-compose.yml",[3138,8974,8872],{"class":3725},[3138,8976,8977],{"class":3687}," docker-compose.prod.yml",[3138,8979,6209],{"class":3687},[3138,8981,8982],{"class":3136,"line":3755},[3138,8983,3772],{"emptyLinePlaceholder":3771},[3138,8985,8986],{"class":3136,"line":3768},[3138,8987,8988],{"class":3697},"# Запустити (Next.js у standalone-режимі)\n",[3138,8990,8991,8993,8995,8997,8999,9001,9003,9005],{"class":3136,"line":3775},[3138,8992,3684],{"class":3683},[3138,8994,3688],{"class":3687},[3138,8996,8872],{"class":3725},[3138,8998,8972],{"class":3687},[3138,9000,8872],{"class":3725},[3138,9002,8977],{"class":3687},[3138,9004,4919],{"class":3687},[3138,9006,4922],{"class":3725},[3114,9008,9009,9012,9013,4673,9016,9019],{},[3335,9010,9011],{},"docker-compose.prod.yml"," має перевизначати ",[3335,9014,9015],{},"target: production",[3335,9017,9018],{},"build",", щоб Compose використовував production-стадію Dockerfile з мінімальним образом.",[3641,9021,9022,9028,9029,9031,9032,9035,9036,9039],{},[3120,9023,9024,9025],{},"Анонімний volume ",[3335,9026,9027],{},"/app/.next"," захищає директорію збірки Next.js аналогічно до ",[3335,9030,5569],{},". Без нього bind mount ",[3335,9033,9034],{},"./web:/app"," перезапише збірку з хоста (де її може не бути), і застосунок не запуститься. Docker зберігає скомпільований ",[3335,9037,9038],{},".next"," всередині контейнера та ніколи не синхронізує його з хостом.",[3416,9041],{},[3423,9043,9045],{"id":9044},"зупинка-та-очищення","Зупинка та очищення",[3423,9047,9045],{"id":9048},"зупинка-та-очищення-1",[3129,9050,9052,9062,9072,9075,9085,9095,9098,9108,9117,9120,9130],{"title":9051},"Зупинка контейнерів",[3133,9053,9055,3143,9058],{"className":9054},[3136],[3138,9056,3142],{"className":9057},[3141],[3138,9059,9061],{"className":9060},[3147],"# Зупинити контейнери (volumes та мережі залишаються)",[3133,9063,9065,3143,9068],{"className":9064},[3136],[3138,9066,3142],{"className":9067},[3141],[3120,9069,9071],{"className":9070},[3158],"docker compose stop",[3133,9073],{"className":9074},[3136],[3133,9076,9078,3143,9081],{"className":9077},[3136],[3138,9079,3142],{"className":9080},[3141],[3138,9082,9084],{"className":9083},[3147],"# Запустити знову",[3133,9086,9088,3143,9091],{"className":9087},[3136],[3138,9089,3142],{"className":9090},[3141],[3120,9092,9094],{"className":9093},[3158],"docker compose start",[3133,9096],{"className":9097},[3136],[3133,9099,9101,3143,9104],{"className":9100},[3136],[3138,9102,3142],{"className":9103},[3141],[3138,9105,9107],{"className":9106},[3147],"# Зупинити та видалити контейнери + мережі (volumes залишаються)",[3133,9109,9111,3143,9114],{"className":9110},[3136],[3138,9112,3142],{"className":9113},[3141],[3120,9115,3477],{"className":9116},[3158],[3133,9118],{"className":9119},[3136],[3133,9121,9123,3143,9126],{"className":9122},[3136],[3138,9124,3142],{"className":9125},[3141],[3138,9127,9129],{"className":9128},[3147],"# Видалити все, включно з volumes (УВАГА: дані будуть втрачені)",[3133,9131,9133,3143,9136],{"className":9132},[3136],[3138,9134,3142],{"className":9135},[3141],[3120,9137,9139],{"className":9138},[3158],"docker compose down -v",[5924,9141,9142,9147],{},[3120,9143,9144,9146],{},[3335,9145,9139],{}," видаляє volumes:"," Всі дані у базі даних та WordPress будуть втрачені. Використовуйте цю команду лише для повного очищення development-середовища.",[3416,9149],{},[3109,9151,9153],{"id":9152},"конфігурація-сервісів-детальний-розбір","Конфігурація сервісів: детальний розбір",[3423,9155,9157],{"id":9156},"build-збірка-образів-через-compose","Build: збірка образів через Compose",[3114,9159,9160,9161,9163],{},"Замість використання готових образів (",[3335,9162,4660],{},"), Compose може збирати образи з Dockerfile.",[3114,9165,9166],{},[3120,9167,5122],{},[3670,9169,9172],{"className":9170,"code":9171,"language":5082},[5080],"myapp/\n├── docker-compose.yml\n├── backend/\n│   ├── Dockerfile\n│   └── src/\n└── frontend/\n    ├── Dockerfile\n    └── src/\n",[3335,9173,9171],{"__ignoreMap":3675},[3114,9175,9176],{},[3120,9177,4293],{},[3670,9179,9181],{"className":3823,"code":9180,"language":3825,"meta":3675,"style":3675},"services:\n  backend:\n    build:\n      context: ./backend\n      dockerfile: Dockerfile\n      args:\n        - NODE_ENV=development\n    ports:\n      - \"5000:5000\"\n    environment:\n      - DATABASE_URL=postgres://db:5432/myapp\n\n  frontend:\n    build: ./frontend  # Скорочений синтаксис\n    ports:\n      - \"3000:3000\"\n    environment:\n      - REACT_APP_API_URL=http://localhost:5000\n",[3335,9182,9183,9189,9195,9201,9210,9218,9225,9233,9239,9246,9252,9259,9263,9269,9281,9287,9293,9299],{"__ignoreMap":3675},[3138,9184,9185,9187],{"class":3136,"line":3680},[3138,9186,3833],{"class":3832},[3138,9188,3837],{"class":3836},[3138,9190,9191,9193],{"class":3136,"line":3694},[3138,9192,4590],{"class":3832},[3138,9194,3837],{"class":3836},[3138,9196,9197,9199],{"class":3136,"line":3755},[3138,9198,6874],{"class":3832},[3138,9200,3837],{"class":3836},[3138,9202,9203,9205,9207],{"class":3136,"line":3768},[3138,9204,6881],{"class":3832},[3138,9206,3852],{"class":3836},[3138,9208,9209],{"class":3855},"./backend\n",[3138,9211,9212,9214,9216],{"class":3136,"line":3775},[3138,9213,6891],{"class":3832},[3138,9215,3852],{"class":3836},[3138,9217,6896],{"class":3855},[3138,9219,9220,9223],{"class":3136,"line":3781},[3138,9221,9222],{"class":3832},"      args",[3138,9224,3837],{"class":3836},[3138,9226,9227,9230],{"class":3136,"line":4165},[3138,9228,9229],{"class":3836},"        - ",[3138,9231,9232],{"class":3855},"NODE_ENV=development\n",[3138,9234,9235,9237],{"class":3136,"line":4178},[3138,9236,3861],{"class":3832},[3138,9238,3837],{"class":3836},[3138,9240,9241,9243],{"class":3136,"line":4189},[3138,9242,3868],{"class":3836},[3138,9244,9245],{"class":3687},"\"5000:5000\"\n",[3138,9247,9248,9250],{"class":3136,"line":4377},[3138,9249,4325],{"class":3832},[3138,9251,3837],{"class":3836},[3138,9253,9254,9256],{"class":3136,"line":4385},[3138,9255,3868],{"class":3836},[3138,9257,9258],{"class":3855},"DATABASE_URL=postgres://db:5432/myapp\n",[3138,9260,9261],{"class":3136,"line":4393},[3138,9262,3772],{"emptyLinePlaceholder":3771},[3138,9264,9265,9267],{"class":3136,"line":4401},[3138,9266,4582],{"class":3832},[3138,9268,3837],{"class":3836},[3138,9270,9271,9273,9275,9278],{"class":3136,"line":4406},[3138,9272,6874],{"class":3832},[3138,9274,3852],{"class":3836},[3138,9276,9277],{"class":3855},"./frontend",[3138,9279,9280],{"class":3697},"  # Скорочений синтаксис\n",[3138,9282,9283,9285],{"class":3136,"line":4414},[3138,9284,3861],{"class":3832},[3138,9286,3837],{"class":3836},[3138,9288,9289,9291],{"class":3136,"line":4424},[3138,9290,3868],{"class":3836},[3138,9292,5217],{"class":3687},[3138,9294,9295,9297],{"class":3136,"line":4431},[3138,9296,4325],{"class":3832},[3138,9298,3837],{"class":3836},[3138,9300,9301,9303],{"class":3136,"line":4438},[3138,9302,3868],{"class":3836},[3138,9304,9305],{"class":3855},"REACT_APP_API_URL=http://localhost:5000\n",[3114,9307,9308],{},[3120,9309,9310,9311,3820],{},"Пояснення ",[3335,9312,9018],{},[4204,9314,9315,9319,9327],{},[4207,9316,9318],{"name":9317,"type":5591},"context","Шлях до директорії, яка буде використовуватися як build context (там, де знаходяться файли для збірки).",[4207,9320,9321,9322,4673,9325,5816],{"name":6193,"type":5591},"Альтернативна назва файлу інструкцій збірки (за замовчуванням Docker шукає ",[3335,9323,9324],{},"Dockerfile",[3335,9326,9317],{},[4207,9328,9330,9331,9334,9335,5816],{"name":6342,"type":9329},"array|object","Build arguments (аргументи збірки), які передаються у Dockerfile як директиви ",[3335,9332,9333],{},"ARG",". Вони існують лише на етапі збірки і не потрапляють у фінальний образ (на відміну від ",[3335,9336,4668],{},[3114,9338,9339],{},[3120,9340,9341],{},"Збірка образів:",[3670,9343,9345],{"className":3672,"code":9344,"language":3674,"meta":3675,"style":3675},"# Зібрати образи та запустити\ndocker compose up --build\n\n# Лише зібрати образи (без запуску)\ndocker compose build\n\n# Зібрати конкретний сервіс\ndocker compose build backend\n",[3335,9346,9347,9352,9363,9367,9372,9380,9384,9389],{"__ignoreMap":3675},[3138,9348,9349],{"class":3136,"line":3680},[3138,9350,9351],{"class":3697},"# Зібрати образи та запустити\n",[3138,9353,9354,9356,9358,9360],{"class":3136,"line":3694},[3138,9355,3684],{"class":3683},[3138,9357,3688],{"class":3687},[3138,9359,4919],{"class":3687},[3138,9361,9362],{"class":3725}," --build\n",[3138,9364,9365],{"class":3136,"line":3755},[3138,9366,3772],{"emptyLinePlaceholder":3771},[3138,9368,9369],{"class":3136,"line":3768},[3138,9370,9371],{"class":3697},"# Лише зібрати образи (без запуску)\n",[3138,9373,9374,9376,9378],{"class":3136,"line":3775},[3138,9375,3684],{"class":3683},[3138,9377,3688],{"class":3687},[3138,9379,6209],{"class":3687},[3138,9381,9382],{"class":3136,"line":3781},[3138,9383,3772],{"emptyLinePlaceholder":3771},[3138,9385,9386],{"class":3136,"line":4165},[3138,9387,9388],{"class":3697},"# Зібрати конкретний сервіс\n",[3138,9390,9391,9393,9395,9398],{"class":3136,"line":4178},[3138,9392,3684],{"class":3683},[3138,9394,3688],{"class":3687},[3138,9396,9397],{"class":3687}," build",[3138,9399,9400],{"class":3687}," backend\n",[3114,9402,9403],{},[3120,9404,9405,9406,3820],{},"Коли використовувати ",[3335,9407,9018],{},[3325,9409,9410,9413,9416],{},[3328,9411,9412],{},"Розробка власного застосунку (не готовий образ з Docker Hub)",[3328,9414,9415],{},"Потреба у кастомізації базового образу",[3328,9417,9418],{},"Multi-stage builds для оптимізації розміру",[3114,9420,9421],{},[3120,9422,9405,9423,3820],{},[3335,9424,4660],{},[3325,9426,9427,9430],{},[3328,9428,9429],{},"Використання готових сервісів (PostgreSQL, Redis, Nginx)",[3328,9431,9432],{},"Production deployment (образи вже зібрані у CI/CD)",[3641,9434,9435,9445,9474],{},[3114,9436,9437,9444],{},[3120,9438,9439,9440,5878,9442,3820],{},"Комбінація ",[3335,9441,9018],{},[3335,9443,4660],{}," Ви можете вказати обидва параметри — Compose зібере образ та присвоїть йому вказане ім'я:",[3670,9446,9448],{"className":3823,"code":9447,"language":3825,"meta":3675,"style":3675},"backend:\n  build: ./backend\n  image: myapp-backend:latest\n",[3335,9449,9450,9456,9465],{"__ignoreMap":3675},[3138,9451,9452,9454],{"class":3136,"line":3680},[3138,9453,4701],{"class":3832},[3138,9455,3837],{"class":3836},[3138,9457,9458,9461,9463],{"class":3136,"line":3694},[3138,9459,9460],{"class":3832},"  build",[3138,9462,3852],{"class":3836},[3138,9464,9209],{"class":3855},[3138,9466,9467,9469,9471],{"class":3136,"line":3755},[3138,9468,4622],{"class":3832},[3138,9470,3852],{"class":3836},[3138,9472,9473],{"class":3855},"myapp-backend:latest\n",[3114,9475,9476],{},"Це зручно для подальшого push до registry.",[3423,9478,9480],{"id":9479},"environment-змінні-середовища","Environment: змінні середовища",[3412,9482,9484,9495],{"title":9483},"Методологія The Twelve-Factor App",[3114,9485,9486,9487,9490,9491,9494],{},"Згідно з третім правилом всесвітньо визнаної методології ",[3120,9488,9489],{},"The Twelve-Factor App",", конфігурація застосунку (паролі, URL-адреси баз даних, API ключі) має зберігатися виключно у ",[3120,9492,9493],{},"змінних середовища"," (Environment Variables).",[3114,9496,9497,9498,9500],{},"Хардкод конфігурації в коді або навіть жорстко зашиті значення у ",[3335,9499,3409],{}," (без можливості перевизначення) — це антипатерн. Образ вашого застосунку має бути абсолютно ідентичним (portable) для Development, Staging та Production. Різниця між цими середовищами має полягати лише у переданих змінних середовища.",[3114,9502,9503],{},"Compose підтримує кілька способів передачі змінних середовища у контейнери.",[3114,9505,9506],{},[3120,9507,9508,9509,3820],{},"1. Inline у ",[3335,9510,3409],{},[3670,9512,9514],{"className":3823,"code":9513,"language":3825,"meta":3675,"style":3675},"services:\n  web:\n    image: myapp\n    environment:\n      - NODE_ENV=production\n      - API_KEY=secret123\n",[3335,9515,9516,9522,9528,9537,9543,9550],{"__ignoreMap":3675},[3138,9517,9518,9520],{"class":3136,"line":3680},[3138,9519,3833],{"class":3832},[3138,9521,3837],{"class":3836},[3138,9523,9524,9526],{"class":3136,"line":3694},[3138,9525,3842],{"class":3832},[3138,9527,3837],{"class":3836},[3138,9529,9530,9532,9534],{"class":3136,"line":3755},[3138,9531,3849],{"class":3832},[3138,9533,3852],{"class":3836},[3138,9535,9536],{"class":3855},"myapp\n",[3138,9538,9539,9541],{"class":3136,"line":3768},[3138,9540,4325],{"class":3832},[3138,9542,3837],{"class":3836},[3138,9544,9545,9547],{"class":3136,"line":3775},[3138,9546,3868],{"class":3836},[3138,9548,9549],{"class":3855},"NODE_ENV=production\n",[3138,9551,9552,9554],{"class":3136,"line":3781},[3138,9553,3868],{"class":3836},[3138,9555,9556],{"class":3855},"API_KEY=secret123\n",[3114,9558,9559],{},[3120,9560,9561,9562,9564],{},"2. Через ",[3335,9563,7274],{}," файл (рекомендовано):",[3114,9566,9567],{},[3120,9568,9569,9570,9572],{},"Створити ",[3335,9571,7274],{}," у тій же директорії:",[3670,9574,9576],{"className":7272,"code":9575,"language":5712,"meta":3675,"style":3675},"NODE_ENV=production\nAPI_KEY=secret123\nDATABASE_URL=postgres://db:5432/myapp\n",[3335,9577,9578,9582,9586],{"__ignoreMap":3675},[3138,9579,9580],{"class":3136,"line":3680},[3138,9581,9549],{},[3138,9583,9584],{"class":3136,"line":3694},[3138,9585,9556],{},[3138,9587,9588],{"class":3136,"line":3755},[3138,9589,9258],{},[3114,9591,9592],{},[3120,9593,4293],{},[3670,9595,9597],{"className":3823,"code":9596,"language":3825,"meta":3675,"style":3675},"services:\n  web:\n    image: myapp\n    environment:\n      - NODE_ENV=${NODE_ENV}\n      - API_KEY=${API_KEY}\n      - DATABASE_URL\n",[3335,9598,9599,9605,9611,9619,9625,9632,9639],{"__ignoreMap":3675},[3138,9600,9601,9603],{"class":3136,"line":3680},[3138,9602,3833],{"class":3832},[3138,9604,3837],{"class":3836},[3138,9606,9607,9609],{"class":3136,"line":3694},[3138,9608,3842],{"class":3832},[3138,9610,3837],{"class":3836},[3138,9612,9613,9615,9617],{"class":3136,"line":3755},[3138,9614,3849],{"class":3832},[3138,9616,3852],{"class":3836},[3138,9618,9536],{"class":3855},[3138,9620,9621,9623],{"class":3136,"line":3768},[3138,9622,4325],{"class":3832},[3138,9624,3837],{"class":3836},[3138,9626,9627,9629],{"class":3136,"line":3775},[3138,9628,3868],{"class":3836},[3138,9630,9631],{"class":3855},"NODE_ENV=${NODE_ENV}\n",[3138,9633,9634,9636],{"class":3136,"line":3781},[3138,9635,3868],{"class":3836},[3138,9637,9638],{"class":3855},"API_KEY=${API_KEY}\n",[3138,9640,9641,9643],{"class":3136,"line":4165},[3138,9642,3868],{"class":3836},[3138,9644,9645],{"class":3855},"DATABASE_URL\n",[3114,9647,9648,9649,3394],{},"Compose автоматично підставить значення з ",[3335,9650,7274],{},[3114,9652,9653],{},[3120,9654,9655],{},"3. Через окремий env-файл:",[3670,9657,9659],{"className":3823,"code":9658,"language":3825,"meta":3675,"style":3675},"services:\n  web:\n    image: myapp\n    env_file:\n      - ./config/production.env\n      - ./config/secrets.env\n",[3335,9660,9661,9667,9673,9681,9688,9695],{"__ignoreMap":3675},[3138,9662,9663,9665],{"class":3136,"line":3680},[3138,9664,3833],{"class":3832},[3138,9666,3837],{"class":3836},[3138,9668,9669,9671],{"class":3136,"line":3694},[3138,9670,3842],{"class":3832},[3138,9672,3837],{"class":3836},[3138,9674,9675,9677,9679],{"class":3136,"line":3755},[3138,9676,3849],{"class":3832},[3138,9678,3852],{"class":3836},[3138,9680,9536],{"class":3855},[3138,9682,9683,9686],{"class":3136,"line":3768},[3138,9684,9685],{"class":3832},"    env_file",[3138,9687,3837],{"class":3836},[3138,9689,9690,9692],{"class":3136,"line":3775},[3138,9691,3868],{"class":3836},[3138,9693,9694],{"class":3855},"./config/production.env\n",[3138,9696,9697,9699],{"class":3136,"line":3781},[3138,9698,3868],{"class":3836},[3138,9700,9701],{"class":3855},"./config/secrets.env\n",[3114,9703,9704],{},[3120,9705,9706],{},"Пріоритет змінних:",[3442,9708,9709,9717,9722,9727],{},[3328,9710,9711,9712,4673,9714,9716],{},"Змінні з ",[3335,9713,4668],{},[3335,9715,3409],{}," (найвищий)",[3328,9718,9711,9719],{},[3335,9720,9721],{},"env_file",[3328,9723,9711,9724,9726],{},[3335,9725,7274],{}," файлу",[3328,9728,9729],{},"Змінні середовища хоста",[5924,9731,9732,9751],{},[3114,9733,9734,9740,9741,4687,9743,9746,9747,9750],{},[3120,9735,9736,9737,9739],{},"Не комітьте ",[3335,9738,7274],{}," з секретами:"," Додайте ",[3335,9742,7274],{},[3335,9744,9745],{},".gitignore",". Для команди створіть ",[3335,9748,9749],{},".env.example"," з placeholder-значеннями:",[3670,9752,9754],{"className":7272,"code":9753,"language":5712,"meta":3675,"style":3675},"NODE_ENV=development\nAPI_KEY=your_api_key_here\nDATABASE_URL=postgres://db:5432/myapp\n",[3335,9755,9756,9760,9765],{"__ignoreMap":3675},[3138,9757,9758],{"class":3136,"line":3680},[3138,9759,9232],{},[3138,9761,9762],{"class":3136,"line":3694},[3138,9763,9764],{},"API_KEY=your_api_key_here\n",[3138,9766,9767],{"class":3136,"line":3755},[3138,9768,9258],{},[3423,9770,9772],{"id":9771},"volumes-монтування-даних","Volumes: монтування даних",[3412,9774,9776,9786],{"title":9775},"Філософія ефемерності контейнерів",[3114,9777,9778,9779,9782,9783,9785],{},"Контейнер слід сприймати не як віртуальну машину з жорстким диском, а як ",[3120,9780,9781],{},"тимчасовий процес",". Будь-які файли, які контейнер створює чи змінює у своїй внутрішній файловій системі (наприклад, записи в базі даних, завантажені картинки), існують лише доти, доки існує сам контейнер. Якщо контейнер видалити (",[3335,9784,3477],{},"), ці дані зникнуть назавжди.",[3114,9787,9788,9791,9792,9795],{},[3120,9789,9790],{},"Метафора:"," Уявіть, що контейнер — це комп'ютер без власного диска, який працює лише в оперативній пам'яті. ",[3120,9793,9794],{},"Volume"," — це \"USB-флешка\" або зовнішній диск, який ми підключаємо до цього комп'ютера. Навіть якщо комп'ютер вимкнути чи замінити на інший, дані на \"флешці\" залишаться неушкодженими.",[3114,9797,9798],{},"Compose підтримує три типи volumes:",[3114,9800,9801],{},[3120,9802,9803],{},"1. Named Volumes (керовані Docker):",[3670,9805,9807],{"className":3823,"code":9806,"language":3825,"meta":3675,"style":3675},"services:\n  db:\n    image: postgres:16\n    volumes:\n      - db-data:/var/lib/postgresql/data\n\nvolumes:\n  db-data:  # Compose створить volume\n",[3335,9808,9809,9815,9821,9830,9836,9843,9847,9853],{"__ignoreMap":3675},[3138,9810,9811,9813],{"class":3136,"line":3680},[3138,9812,3833],{"class":3832},[3138,9814,3837],{"class":3836},[3138,9816,9817,9819],{"class":3136,"line":3694},[3138,9818,4309],{"class":3832},[3138,9820,3837],{"class":3836},[3138,9822,9823,9825,9827],{"class":3136,"line":3755},[3138,9824,3849],{"class":3832},[3138,9826,3852],{"class":3836},[3138,9828,9829],{"class":3855},"postgres:16\n",[3138,9831,9832,9834],{"class":3136,"line":3768},[3138,9833,4372],{"class":3832},[3138,9835,3837],{"class":3836},[3138,9837,9838,9840],{"class":3136,"line":3775},[3138,9839,3868],{"class":3836},[3138,9841,9842],{"class":3855},"db-data:/var/lib/postgresql/data\n",[3138,9844,9845],{"class":3136,"line":3781},[3138,9846,3772],{"emptyLinePlaceholder":3771},[3138,9848,9849,9851],{"class":3136,"line":4165},[3138,9850,4217],{"class":3832},[3138,9852,3837],{"class":3836},[3138,9854,9855,9857,9860],{"class":3136,"line":4178},[3138,9856,4554],{"class":3832},[3138,9858,9859],{"class":3836},":  ",[3138,9861,9862],{"class":3697},"# Compose створить volume\n",[3114,9864,9865],{},[3120,9866,9867],{},"2. Bind Mounts (директорії хоста):",[3670,9869,9871],{"className":3823,"code":9870,"language":3825,"meta":3675,"style":3675},"services:\n  web:\n    image: nginx\n    volumes:\n      - ./nginx.conf:/etc/nginx/nginx.conf:ro  # read-only\n      - ./html:/usr/share/nginx/html\n",[3335,9872,9873,9879,9885,9894,9900,9910],{"__ignoreMap":3675},[3138,9874,9875,9877],{"class":3136,"line":3680},[3138,9876,3833],{"class":3832},[3138,9878,3837],{"class":3836},[3138,9880,9881,9883],{"class":3136,"line":3694},[3138,9882,3842],{"class":3832},[3138,9884,3837],{"class":3836},[3138,9886,9887,9889,9891],{"class":3136,"line":3755},[3138,9888,3849],{"class":3832},[3138,9890,3852],{"class":3836},[3138,9892,9893],{"class":3855},"nginx\n",[3138,9895,9896,9898],{"class":3136,"line":3768},[3138,9897,4372],{"class":3832},[3138,9899,3837],{"class":3836},[3138,9901,9902,9904,9907],{"class":3136,"line":3775},[3138,9903,3868],{"class":3836},[3138,9905,9906],{"class":3855},"./nginx.conf:/etc/nginx/nginx.conf:ro",[3138,9908,9909],{"class":3697},"  # read-only\n",[3138,9911,9912,9914],{"class":3136,"line":3781},[3138,9913,3868],{"class":3836},[3138,9915,9916],{"class":3855},"./html:/usr/share/nginx/html\n",[3114,9918,9919],{},[3120,9920,9921],{},"3. Anonymous Volumes:",[3670,9923,9925],{"className":3823,"code":9924,"language":3825,"meta":3675,"style":3675},"services:\n  app:\n    image: myapp\n    volumes:\n      - /app/node_modules  # Анонімний volume\n",[3335,9926,9927,9933,9940,9948,9954],{"__ignoreMap":3675},[3138,9928,9929,9931],{"class":3136,"line":3680},[3138,9930,3833],{"class":3832},[3138,9932,3837],{"class":3836},[3138,9934,9935,9938],{"class":3136,"line":3694},[3138,9936,9937],{"class":3832},"  app",[3138,9939,3837],{"class":3836},[3138,9941,9942,9944,9946],{"class":3136,"line":3755},[3138,9943,3849],{"class":3832},[3138,9945,3852],{"class":3836},[3138,9947,9536],{"class":3855},[3138,9949,9950,9952],{"class":3136,"line":3768},[3138,9951,4372],{"class":3832},[3138,9953,3837],{"class":3836},[3138,9955,9956,9958,9960],{"class":3136,"line":3775},[3138,9957,3868],{"class":3836},[3138,9959,5194],{"class":3855},[3138,9961,9962],{"class":3697},"  # Анонімний volume\n",[3114,9964,9965],{},[3120,9966,9967],{},"Синтаксис:",[3325,9969,9970,9978,9986],{},[3328,9971,9972,3143,9975],{},[3120,9973,9974],{},"Named volume:",[3335,9976,9977],{},"volume-name:/path/in/container",[3328,9979,9980,3143,9983],{},[3120,9981,9982],{},"Bind mount:",[3335,9984,9985],{},"./host/path:/container/path",[3328,9987,9988,9991,9992,9995],{},[3120,9989,9990],{},"Read-only:"," додати ",[3335,9993,9994],{},":ro"," в кінці",[3114,9997,9998],{},[3120,9999,10000],{},"Коли що використовувати:",[7338,10002,10003,10017,10029],{},[7341,10004,10007,10010,10011,3143,10014],{"icon":10005,"title":10006},"i-heroicons-server","Named Volume",[3120,10008,10009],{},"Найкраще для:"," Persistent data (бази даних, завантажені файли користувачів).\nDocker повністю керує життєвим циклом, дані не залежать від ОС хоста.\n",[3120,10012,10013],{},"Приклад:",[3335,10015,10016],{},"postgres-data:/var/lib/postgresql/data",[7341,10018,10021,10023,10024,3143,10026],{"icon":10019,"title":10020},"i-heroicons-folder-open","Bind Mount",[3120,10022,10009],{}," Розробки (hot-reload), підключення конфігураційних файлів.\nМонтує конкретну директорію хоста. Усі зміни миттєво синхронізуються в обидві сторони.\n",[3120,10025,10013],{},[3335,10027,10028],{},"./src:/app/src",[7341,10030,10033,10035,10036,10038,10039,10042,10043,3143,10045],{"icon":10031,"title":10032},"i-heroicons-document-minus","Anonymous Volume",[3120,10034,10009],{}," Тимчасових даних, захисту директорій (наприклад ",[3335,10037,5569],{},") від перезапису через Bind Mount.\nDocker створює volume з випадковим хеш-іменем. Видаляється разом з контейнером (якщо запущено ",[3335,10040,10041],{},"down -v",").\n",[3120,10044,10013],{},[3335,10046,5194],{},[3114,10048,10049],{},[3120,10050,6136],{},[3670,10052,10054],{"className":3823,"code":10053,"language":3825,"meta":3675,"style":3675},"services:\n  backend:\n    build: ./backend\n    volumes:\n      - ./backend/src:/app/src  # Зміни у коді миттєво у контейнері\n      - /app/node_modules        # Не перезаписувати node_modules\n    command: npm run dev\n",[3335,10055,10056,10062,10068,10076,10082,10092,10101],{"__ignoreMap":3675},[3138,10057,10058,10060],{"class":3136,"line":3680},[3138,10059,3833],{"class":3832},[3138,10061,3837],{"class":3836},[3138,10063,10064,10066],{"class":3136,"line":3694},[3138,10065,4590],{"class":3832},[3138,10067,3837],{"class":3836},[3138,10069,10070,10072,10074],{"class":3136,"line":3755},[3138,10071,6874],{"class":3832},[3138,10073,3852],{"class":3836},[3138,10075,9209],{"class":3855},[3138,10077,10078,10080],{"class":3136,"line":3768},[3138,10079,4372],{"class":3832},[3138,10081,3837],{"class":3836},[3138,10083,10084,10086,10089],{"class":3136,"line":3775},[3138,10085,3868],{"class":3836},[3138,10087,10088],{"class":3855},"./backend/src:/app/src",[3138,10090,10091],{"class":3697},"  # Зміни у коді миттєво у контейнері\n",[3138,10093,10094,10096,10098],{"class":3136,"line":3781},[3138,10095,3868],{"class":3836},[3138,10097,5194],{"class":3855},[3138,10099,10100],{"class":3697},"        # Не перезаписувати node_modules\n",[3138,10102,10103,10105,10107],{"class":3136,"line":4165},[3138,10104,5199],{"class":3832},[3138,10106,3852],{"class":3836},[3138,10108,10109],{"class":3855},"npm run dev\n",[3114,10111,10112,10113,10116,10117,10120],{},"Тепер зміни у ",[3335,10114,10115],{},"./backend/src"," на хості миттєво відображаються у контейнері, і ",[3335,10118,10119],{},"npm run dev"," перезапускає сервер.",[3423,10122,10124],{"id":10123},"networks-мережева-конфігурація","Networks: мережева конфігурація",[3412,10126,10128,10149],{"title":10127},"Внутрішній DNS та проблема localhost",[3114,10129,10130,10131,10134,10135,10140,10141,10143,10144,10148],{},"Новачки часто припускаються помилки, намагаючись підключити бекенд до бази даних за адресою ",[3335,10132,10133],{},"localhost:5432",". Важливо розуміти: ",[3120,10136,10137,10138],{},"кожен контейнер має свій власний ",[3335,10139,5937],{},". Якщо бекенд звертається на ",[3335,10142,5937],{},", він шукає базу даних ",[10145,10146,10147],"em",{},"всередині свого власного контейнера",", де її, звісно, немає.",[3114,10150,10151,10152,10155,10156,10159,10160,10163,10164,4272,10166,4272,10168,10170],{},"Замість цього, Docker Compose автоматично створює для вашого проєкту ",[3120,10153,10154],{},"ізольовану bridge-мережу"," і запускає ",[3120,10157,10158],{},"внутрішній DNS-сервер",". Цей DNS-сервер дозволяє контейнерам звертатися один до одного за ",[3120,10161,10162],{},"іменами сервісів"," (наприклад, ",[3335,10165,4605],{},[3335,10167,7614],{},[3335,10169,5585],{},"), які автоматично резолвляться у правильні внутрішні IP-адреси.",[3114,10172,10173],{},[3120,10174,10175],{},"Створення кастомних мереж:",[3670,10177,10179],{"className":3823,"code":10178,"language":3825,"meta":3675,"style":3675},"services:\n  frontend:\n    image: nginx\n    networks:\n      - frontend-net\n\n  backend:\n    image: myapp-api\n    networks:\n      - frontend-net\n      - backend-net\n\n  db:\n    image: postgres:16\n    networks:\n      - backend-net\n\nnetworks:\n  frontend-net:\n    driver: bridge\n  backend-net:\n    driver: bridge\n",[3335,10180,10181,10187,10193,10201,10207,10214,10218,10224,10233,10239,10245,10252,10256,10262,10270,10276,10282,10286,10292,10299,10309,10316],{"__ignoreMap":3675},[3138,10182,10183,10185],{"class":3136,"line":3680},[3138,10184,3833],{"class":3832},[3138,10186,3837],{"class":3836},[3138,10188,10189,10191],{"class":3136,"line":3694},[3138,10190,4582],{"class":3832},[3138,10192,3837],{"class":3836},[3138,10194,10195,10197,10199],{"class":3136,"line":3755},[3138,10196,3849],{"class":3832},[3138,10198,3852],{"class":3836},[3138,10200,9893],{"class":3855},[3138,10202,10203,10205],{"class":3136,"line":3768},[3138,10204,4388],{"class":3832},[3138,10206,3837],{"class":3836},[3138,10208,10209,10211],{"class":3136,"line":3775},[3138,10210,3868],{"class":3836},[3138,10212,10213],{"class":3855},"frontend-net\n",[3138,10215,10216],{"class":3136,"line":3781},[3138,10217,3772],{"emptyLinePlaceholder":3771},[3138,10219,10220,10222],{"class":3136,"line":4165},[3138,10221,4590],{"class":3832},[3138,10223,3837],{"class":3836},[3138,10225,10226,10228,10230],{"class":3136,"line":4178},[3138,10227,3849],{"class":3832},[3138,10229,3852],{"class":3836},[3138,10231,10232],{"class":3855},"myapp-api\n",[3138,10234,10235,10237],{"class":3136,"line":4189},[3138,10236,4388],{"class":3832},[3138,10238,3837],{"class":3836},[3138,10240,10241,10243],{"class":3136,"line":4377},[3138,10242,3868],{"class":3836},[3138,10244,10213],{"class":3855},[3138,10246,10247,10249],{"class":3136,"line":4385},[3138,10248,3868],{"class":3836},[3138,10250,10251],{"class":3855},"backend-net\n",[3138,10253,10254],{"class":3136,"line":4393},[3138,10255,3772],{"emptyLinePlaceholder":3771},[3138,10257,10258,10260],{"class":3136,"line":4401},[3138,10259,4309],{"class":3832},[3138,10261,3837],{"class":3836},[3138,10263,10264,10266,10268],{"class":3136,"line":4406},[3138,10265,3849],{"class":3832},[3138,10267,3852],{"class":3836},[3138,10269,9829],{"class":3855},[3138,10271,10272,10274],{"class":3136,"line":4414},[3138,10273,4388],{"class":3832},[3138,10275,3837],{"class":3836},[3138,10277,10278,10280],{"class":3136,"line":4424},[3138,10279,3868],{"class":3836},[3138,10281,10251],{"class":3855},[3138,10283,10284],{"class":3136,"line":4431},[3138,10285,3772],{"emptyLinePlaceholder":3771},[3138,10287,10288,10290],{"class":3136,"line":4438},[3138,10289,4213],{"class":3832},[3138,10291,3837],{"class":3836},[3138,10293,10294,10297],{"class":3136,"line":4445},[3138,10295,10296],{"class":3832},"  frontend-net",[3138,10298,3837],{"class":3836},[3138,10300,10301,10304,10306],{"class":3136,"line":4456},[3138,10302,10303],{"class":3832},"    driver",[3138,10305,3852],{"class":3836},[3138,10307,10308],{"class":3855},"bridge\n",[3138,10310,10311,10314],{"class":3136,"line":4466},[3138,10312,10313],{"class":3832},"  backend-net",[3138,10315,3837],{"class":3836},[3138,10317,10318,10320,10322],{"class":3136,"line":4476},[3138,10319,10303],{"class":3832},[3138,10321,3852],{"class":3836},[3138,10323,10308],{"class":3855},[3114,10325,10326],{},[3120,10327,10328],{},"Налаштування subnet та gateway:",[3670,10330,10332],{"className":3823,"code":10331,"language":3825,"meta":3675,"style":3675},"networks:\n  custom-net:\n    driver: bridge\n    ipam:\n      config:\n        - subnet: 172.28.0.0/16\n          gateway: 172.28.0.1\n",[3335,10333,10334,10340,10347,10355,10362,10369,10381],{"__ignoreMap":3675},[3138,10335,10336,10338],{"class":3136,"line":3680},[3138,10337,4213],{"class":3832},[3138,10339,3837],{"class":3836},[3138,10341,10342,10345],{"class":3136,"line":3694},[3138,10343,10344],{"class":3832},"  custom-net",[3138,10346,3837],{"class":3836},[3138,10348,10349,10351,10353],{"class":3136,"line":3755},[3138,10350,10303],{"class":3832},[3138,10352,3852],{"class":3836},[3138,10354,10308],{"class":3855},[3138,10356,10357,10360],{"class":3136,"line":3768},[3138,10358,10359],{"class":3832},"    ipam",[3138,10361,3837],{"class":3836},[3138,10363,10364,10367],{"class":3136,"line":3775},[3138,10365,10366],{"class":3832},"      config",[3138,10368,3837],{"class":3836},[3138,10370,10371,10373,10376,10378],{"class":3136,"line":3781},[3138,10372,9229],{"class":3836},[3138,10374,10375],{"class":3832},"subnet",[3138,10377,3852],{"class":3836},[3138,10379,10380],{"class":3855},"172.28.0.0/16\n",[3138,10382,10383,10386,10388],{"class":3136,"line":4165},[3138,10384,10385],{"class":3832},"          gateway",[3138,10387,3852],{"class":3836},[3138,10389,10390],{"class":5389},"172.28.0.1\n",[3114,10392,10393],{},[3120,10394,10395],{},"Статична IP-адреса для контейнера:",[3670,10397,10399],{"className":3823,"code":10398,"language":3825,"meta":3675,"style":3675},"services:\n  web:\n    image: nginx\n    networks:\n      custom-net:\n        ipv4_address: 172.28.0.10\n\nnetworks:\n  custom-net:\n    driver: bridge\n    ipam:\n      config:\n        - subnet: 172.28.0.0/16\n",[3335,10400,10401,10407,10413,10421,10427,10434,10444,10448,10454,10460,10468,10474,10480],{"__ignoreMap":3675},[3138,10402,10403,10405],{"class":3136,"line":3680},[3138,10404,3833],{"class":3832},[3138,10406,3837],{"class":3836},[3138,10408,10409,10411],{"class":3136,"line":3694},[3138,10410,3842],{"class":3832},[3138,10412,3837],{"class":3836},[3138,10414,10415,10417,10419],{"class":3136,"line":3755},[3138,10416,3849],{"class":3832},[3138,10418,3852],{"class":3836},[3138,10420,9893],{"class":3855},[3138,10422,10423,10425],{"class":3136,"line":3768},[3138,10424,4388],{"class":3832},[3138,10426,3837],{"class":3836},[3138,10428,10429,10432],{"class":3136,"line":3775},[3138,10430,10431],{"class":3832},"      custom-net",[3138,10433,3837],{"class":3836},[3138,10435,10436,10439,10441],{"class":3136,"line":3781},[3138,10437,10438],{"class":3832},"        ipv4_address",[3138,10440,3852],{"class":3836},[3138,10442,10443],{"class":5389},"172.28.0.10\n",[3138,10445,10446],{"class":3136,"line":4165},[3138,10447,3772],{"emptyLinePlaceholder":3771},[3138,10449,10450,10452],{"class":3136,"line":4178},[3138,10451,4213],{"class":3832},[3138,10453,3837],{"class":3836},[3138,10455,10456,10458],{"class":3136,"line":4189},[3138,10457,10344],{"class":3832},[3138,10459,3837],{"class":3836},[3138,10461,10462,10464,10466],{"class":3136,"line":4377},[3138,10463,10303],{"class":3832},[3138,10465,3852],{"class":3836},[3138,10467,10308],{"class":3855},[3138,10469,10470,10472],{"class":3136,"line":4385},[3138,10471,10359],{"class":3832},[3138,10473,3837],{"class":3836},[3138,10475,10476,10478],{"class":3136,"line":4393},[3138,10477,10366],{"class":3832},[3138,10479,3837],{"class":3836},[3138,10481,10482,10484,10486,10488],{"class":3136,"line":4401},[3138,10483,9229],{"class":3836},[3138,10485,10375],{"class":3832},[3138,10487,3852],{"class":3836},[3138,10489,10380],{"class":3855},[3114,10491,10492],{},[3120,10493,10494],{},"Підключення до існуючої мережі:",[3670,10496,10498],{"className":3823,"code":10497,"language":3825,"meta":3675,"style":3675},"networks:\n  existing-network:\n    external: true\n    name: my-pre-existing-network\n",[3335,10499,10500,10506,10513,10523],{"__ignoreMap":3675},[3138,10501,10502,10504],{"class":3136,"line":3680},[3138,10503,4213],{"class":3832},[3138,10505,3837],{"class":3836},[3138,10507,10508,10511],{"class":3136,"line":3694},[3138,10509,10510],{"class":3832},"  existing-network",[3138,10512,3837],{"class":3836},[3138,10514,10515,10518,10520],{"class":3136,"line":3755},[3138,10516,10517],{"class":3832},"    external",[3138,10519,3852],{"class":3836},[3138,10521,10522],{"class":3725},"true\n",[3138,10524,10525,10528,10530],{"class":3136,"line":3768},[3138,10526,10527],{"class":3832},"    name",[3138,10529,3852],{"class":3836},[3138,10531,10532],{"class":3855},"my-pre-existing-network\n",[3114,10534,10535],{},"Compose не створить цю мережу, а підключиться до вже існуючої.",[3423,10537,10539],{"id":10538},"ports-проброс-портів","Ports: проброс портів",[3114,10541,10542],{},[3120,10543,9967],{},[3670,10545,10547],{"className":3823,"code":10546,"language":3825,"meta":3675,"style":3675},"services:\n  web:\n    image: nginx\n    ports:\n      - \"8080:80\"           # HOST:CONTAINER\n      - \"127.0.0.1:8443:443\"  # Прив'язка до localhost\n      - \"3000-3005:3000-3005\" # Діапазон портів\n      - \"9000\"              # Випадковий порт хоста\n",[3335,10548,10549,10555,10561,10569,10575,10584,10594,10604],{"__ignoreMap":3675},[3138,10550,10551,10553],{"class":3136,"line":3680},[3138,10552,3833],{"class":3832},[3138,10554,3837],{"class":3836},[3138,10556,10557,10559],{"class":3136,"line":3694},[3138,10558,3842],{"class":3832},[3138,10560,3837],{"class":3836},[3138,10562,10563,10565,10567],{"class":3136,"line":3755},[3138,10564,3849],{"class":3832},[3138,10566,3852],{"class":3836},[3138,10568,9893],{"class":3855},[3138,10570,10571,10573],{"class":3136,"line":3768},[3138,10572,3861],{"class":3832},[3138,10574,3837],{"class":3836},[3138,10576,10577,10579,10581],{"class":3136,"line":3775},[3138,10578,3868],{"class":3836},[3138,10580,4194],{"class":3687},[3138,10582,10583],{"class":3697},"           # HOST:CONTAINER\n",[3138,10585,10586,10588,10591],{"class":3136,"line":3781},[3138,10587,3868],{"class":3836},[3138,10589,10590],{"class":3687},"\"127.0.0.1:8443:443\"",[3138,10592,10593],{"class":3697},"  # Прив'язка до localhost\n",[3138,10595,10596,10598,10601],{"class":3136,"line":4165},[3138,10597,3868],{"class":3836},[3138,10599,10600],{"class":3687},"\"3000-3005:3000-3005\"",[3138,10602,10603],{"class":3697}," # Діапазон портів\n",[3138,10605,10606,10608,10611],{"class":3136,"line":4178},[3138,10607,3868],{"class":3836},[3138,10609,10610],{"class":3687},"\"9000\"",[3138,10612,10613],{"class":3697},"              # Випадковий порт хоста\n",[3114,10615,10616],{},[3120,10617,10618],{},"Long syntax (детальний контроль):",[3670,10620,10622],{"className":3823,"code":10621,"language":3825,"meta":3675,"style":3675},"services:\n  web:\n    image: nginx\n    ports:\n      - target: 80        # Порт контейнера\n        published: 8080   # Порт хоста\n        protocol: tcp\n        mode: host\n",[3335,10623,10624,10630,10636,10644,10650,10664,10676,10686],{"__ignoreMap":3675},[3138,10625,10626,10628],{"class":3136,"line":3680},[3138,10627,3833],{"class":3832},[3138,10629,3837],{"class":3836},[3138,10631,10632,10634],{"class":3136,"line":3694},[3138,10633,3842],{"class":3832},[3138,10635,3837],{"class":3836},[3138,10637,10638,10640,10642],{"class":3136,"line":3755},[3138,10639,3849],{"class":3832},[3138,10641,3852],{"class":3836},[3138,10643,9893],{"class":3855},[3138,10645,10646,10648],{"class":3136,"line":3768},[3138,10647,3861],{"class":3832},[3138,10649,3837],{"class":3836},[3138,10651,10652,10654,10657,10659,10661],{"class":3136,"line":3775},[3138,10653,3868],{"class":3836},[3138,10655,10656],{"class":3832},"target",[3138,10658,3852],{"class":3836},[3138,10660,3983],{"class":5389},[3138,10662,10663],{"class":3697},"        # Порт контейнера\n",[3138,10665,10666,10669,10671,10673],{"class":3136,"line":3781},[3138,10667,10668],{"class":3832},"        published",[3138,10670,3852],{"class":3836},[3138,10672,3987],{"class":5389},[3138,10674,10675],{"class":3697},"   # Порт хоста\n",[3138,10677,10678,10681,10683],{"class":3136,"line":4165},[3138,10679,10680],{"class":3832},"        protocol",[3138,10682,3852],{"class":3836},[3138,10684,10685],{"class":3855},"tcp\n",[3138,10687,10688,10691,10693],{"class":3136,"line":4178},[3138,10689,10690],{"class":3832},"        mode",[3138,10692,3852],{"class":3836},[3138,10694,10695],{"class":3855},"host\n",[3114,10697,10698],{},[3120,10699,10700],{},"Expose (без проброса на хост):",[3670,10702,10704],{"className":3823,"code":10703,"language":3825,"meta":3675,"style":3675},"services:\n  backend:\n    image: myapp-api\n    expose:\n      - \"5000\"  # Доступно лише всередині Docker-мережі\n",[3335,10705,10706,10712,10718,10726,10733],{"__ignoreMap":3675},[3138,10707,10708,10710],{"class":3136,"line":3680},[3138,10709,3833],{"class":3832},[3138,10711,3837],{"class":3836},[3138,10713,10714,10716],{"class":3136,"line":3694},[3138,10715,4590],{"class":3832},[3138,10717,3837],{"class":3836},[3138,10719,10720,10722,10724],{"class":3136,"line":3755},[3138,10721,3849],{"class":3832},[3138,10723,3852],{"class":3836},[3138,10725,10232],{"class":3855},[3138,10727,10728,10731],{"class":3136,"line":3768},[3138,10729,10730],{"class":3832},"    expose",[3138,10732,3837],{"class":3836},[3138,10734,10735,10737,10740],{"class":3136,"line":3775},[3138,10736,3868],{"class":3836},[3138,10738,10739],{"class":3687},"\"5000\"",[3138,10741,10742],{"class":3697},"  # Доступно лише всередині Docker-мережі\n",[3114,10744,10745,10748],{},[3335,10746,10747],{},"expose"," документує, які порти використовує сервіс, але не пробросує їх на хост.",[3423,10750,10752],{"id":10751},"depends_on-залежності-між-сервісами","Depends_on: залежності між сервісами",[3114,10754,10755],{},[3120,10756,10757],{},"Базовий синтаксис:",[3670,10759,10761],{"className":3823,"code":10760,"language":3825,"meta":3675,"style":3675},"services:\n  web:\n    image: nginx\n    depends_on:\n      - backend\n      - db\n\n  backend:\n    image: myapp-api\n    depends_on:\n      - db\n\n  db:\n    image: postgres:16\n",[3335,10762,10763,10769,10775,10783,10789,10795,10801,10805,10811,10819,10825,10831,10835,10841],{"__ignoreMap":3675},[3138,10764,10765,10767],{"class":3136,"line":3680},[3138,10766,3833],{"class":3832},[3138,10768,3837],{"class":3836},[3138,10770,10771,10773],{"class":3136,"line":3694},[3138,10772,3842],{"class":3832},[3138,10774,3837],{"class":3836},[3138,10776,10777,10779,10781],{"class":3136,"line":3755},[3138,10778,3849],{"class":3832},[3138,10780,3852],{"class":3836},[3138,10782,9893],{"class":3855},[3138,10784,10785,10787],{"class":3136,"line":3768},[3138,10786,4526],{"class":3832},[3138,10788,3837],{"class":3836},[3138,10790,10791,10793],{"class":3136,"line":3775},[3138,10792,3868],{"class":3836},[3138,10794,4398],{"class":3855},[3138,10796,10797,10799],{"class":3136,"line":3781},[3138,10798,3868],{"class":3836},[3138,10800,4536],{"class":3855},[3138,10802,10803],{"class":3136,"line":4165},[3138,10804,3772],{"emptyLinePlaceholder":3771},[3138,10806,10807,10809],{"class":3136,"line":4178},[3138,10808,4590],{"class":3832},[3138,10810,3837],{"class":3836},[3138,10812,10813,10815,10817],{"class":3136,"line":4189},[3138,10814,3849],{"class":3832},[3138,10816,3852],{"class":3836},[3138,10818,10232],{"class":3855},[3138,10820,10821,10823],{"class":3136,"line":4377},[3138,10822,4526],{"class":3832},[3138,10824,3837],{"class":3836},[3138,10826,10827,10829],{"class":3136,"line":4385},[3138,10828,3868],{"class":3836},[3138,10830,4536],{"class":3855},[3138,10832,10833],{"class":3136,"line":4393},[3138,10834,3772],{"emptyLinePlaceholder":3771},[3138,10836,10837,10839],{"class":3136,"line":4401},[3138,10838,4309],{"class":3832},[3138,10840,3837],{"class":3836},[3138,10842,10843,10845,10847],{"class":3136,"line":4406},[3138,10844,3849],{"class":3832},[3138,10846,3852],{"class":3836},[3138,10848,9829],{"class":3855},[3114,10850,10851],{},[3120,10852,10853,10854,3820],{},"Що робить ",[3335,10855,4790],{},[3442,10857,10858,10871],{},[3328,10859,10860,10863,10864,10866,10867,10866,10869],{},[3120,10861,10862],{},"Порядок запуску:"," Compose запустить ",[3335,10865,4605],{}," → ",[3335,10868,4701],{},[3335,10870,3976],{},[3328,10872,10873,10876,10877,10866,10879,10866,10881],{},[3120,10874,10875],{},"Порядок зупинки:"," Compose зупинить ",[3335,10878,3976],{},[3335,10880,4701],{},[3335,10882,4605],{},[3114,10884,10885],{},[3120,10886,10887,10888,3820],{},"Що НЕ робить ",[3335,10889,4790],{},[3325,10891,10892],{},[3328,10893,10894,10897,10898,10900,10901,10903],{},[3120,10895,10896],{},"Не чекає готовності сервісу"," — Compose запустить ",[3335,10899,4701],{}," одразу після запуску контейнера ",[3335,10902,4605],{},", але PostgreSQL всередині може ще ініціалізуватися",[3114,10905,10906,10909],{},[3120,10907,10908],{},"Проблема: Гонки станів (Race Conditions)","\nБекенд спробує підключитися до бази даних за мілісекунди після старту контейнера. Але базі даних потрібні секунди на ініціалізацію (виділення пам'яті, створення файлів). Відбувається гонка: бекенд приходить швидше, ніж база готова приймати з'єднання, і падає з помилкою.",[3412,10911,10913,10914,10917],{"title":10912},"Crash-Only Software та стійкість","Сучасна архітектура мікросервісів базується на принципі ",[3120,10915,10916],{},"Crash-Only Software",". Це означає, що ваш бекенд має бути готовим до того, що база даних впаде в будь-який момент, або ще не буде готова при старті. Вирішувати цю проблему виключно засобами інфраструктури (наприклад, змушуючи Compose довго чекати) — це антипатерн. Ваш код має вміти самостійно відновлювати з'єднання.",[3114,10919,10920],{},[3120,10921,10922],{},"Рішення 1: Retry logic у додатку",[3114,10924,10925],{},"Додайте логіку повторних спроб підключення у код:",[3670,10927,10929],{"className":6293,"code":10928,"language":6296,"meta":3675,"style":3675},"// C# приклад\nvar retries = 5;\nwhile (retries > 0)\n{\n    try\n    {\n        await dbContext.Database.CanConnectAsync();\n        break;\n    }\n    catch\n    {\n        retries--;\n        await Task.Delay(2000);\n    }\n}\n",[3335,10930,10931,10936,10950,10968,10972,10977,10982,11001,11008,11013,11018,11022,11030,11049,11053],{"__ignoreMap":3675},[3138,10932,10933],{"class":3136,"line":3680},[3138,10934,10935],{"class":3697},"// C# приклад\n",[3138,10937,10938,10940,10943,10945,10948],{"class":3136,"line":3694},[3138,10939,6324],{"class":3725},[3138,10941,10942],{"class":5629}," retries",[3138,10944,5671],{"class":3836},[3138,10946,10947],{"class":5389},"5",[3138,10949,6315],{"class":3836},[3138,10951,10952,10955,10957,10960,10963,10966],{"class":3136,"line":3755},[3138,10953,10954],{"class":5625},"while",[3138,10956,6450],{"class":3836},[3138,10958,10959],{"class":5629},"retries",[3138,10961,10962],{"class":3836}," > ",[3138,10964,10965],{"class":5389},"0",[3138,10967,5719],{"class":3836},[3138,10969,10970],{"class":3136,"line":3768},[3138,10971,5957],{"class":3836},[3138,10973,10974],{"class":3136,"line":3775},[3138,10975,10976],{"class":5625},"    try\n",[3138,10978,10979],{"class":3136,"line":3781},[3138,10980,10981],{"class":3836},"    {\n",[3138,10983,10984,10987,10990,10992,10994,10996,10999],{"class":3136,"line":4165},[3138,10985,10986],{"class":3725},"        await",[3138,10988,10989],{"class":5629}," dbContext",[3138,10991,3394],{"class":3836},[3138,10993,6515],{"class":5629},[3138,10995,3394],{"class":3836},[3138,10997,10998],{"class":3683},"CanConnectAsync",[3138,11000,6434],{"class":3836},[3138,11002,11003,11006],{"class":3136,"line":4178},[3138,11004,11005],{"class":5625},"        break",[3138,11007,6315],{"class":3836},[3138,11009,11010],{"class":3136,"line":4189},[3138,11011,11012],{"class":3836},"    }\n",[3138,11014,11015],{"class":3136,"line":4377},[3138,11016,11017],{"class":5625},"    catch\n",[3138,11019,11020],{"class":3136,"line":4385},[3138,11021,10981],{"class":3836},[3138,11023,11024,11027],{"class":3136,"line":4393},[3138,11025,11026],{"class":5629},"        retries",[3138,11028,11029],{"class":3836},"--;\n",[3138,11031,11032,11034,11037,11039,11042,11044,11047],{"class":3136,"line":4401},[3138,11033,10986],{"class":3725},[3138,11035,11036],{"class":5629}," Task",[3138,11038,3394],{"class":3836},[3138,11040,11041],{"class":3683},"Delay",[3138,11043,5704],{"class":3836},[3138,11045,11046],{"class":5389},"2000",[3138,11048,6345],{"class":3836},[3138,11050,11051],{"class":3136,"line":4406},[3138,11052,11012],{"class":3836},[3138,11054,11055],{"class":3136,"line":4414},[3138,11056,6023],{"class":3836},[3114,11058,11059],{},[3120,11060,11061],{},"Рішення 2: Health checks (Compose V3.4+)",[3670,11063,11065],{"className":3823,"code":11064,"language":3825,"meta":3675,"style":3675},"services:\n  db:\n    image: postgres:16\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n      interval: 5s\n      timeout: 5s\n      retries: 5\n\n  backend:\n    image: myapp-api\n    depends_on:\n      db:\n        condition: service_healthy\n",[3335,11066,11067,11073,11079,11087,11093,11108,11116,11124,11132,11136,11142,11150,11156,11162],{"__ignoreMap":3675},[3138,11068,11069,11071],{"class":3136,"line":3680},[3138,11070,3833],{"class":3832},[3138,11072,3837],{"class":3836},[3138,11074,11075,11077],{"class":3136,"line":3694},[3138,11076,4309],{"class":3832},[3138,11078,3837],{"class":3836},[3138,11080,11081,11083,11085],{"class":3136,"line":3755},[3138,11082,3849],{"class":3832},[3138,11084,3852],{"class":3836},[3138,11086,9829],{"class":3855},[3138,11088,11089,11091],{"class":3136,"line":3768},[3138,11090,5328],{"class":3832},[3138,11092,3837],{"class":3836},[3138,11094,11095,11097,11099,11101,11103,11106],{"class":3136,"line":3775},[3138,11096,5335],{"class":3832},[3138,11098,5338],{"class":3836},[3138,11100,7086],{"class":3687},[3138,11102,4272],{"class":3836},[3138,11104,11105],{"class":3687},"\"pg_isready -U postgres\"",[3138,11107,5359],{"class":3836},[3138,11109,11110,11112,11114],{"class":3136,"line":3781},[3138,11111,5364],{"class":3832},[3138,11113,3852],{"class":3836},[3138,11115,5379],{"class":3855},[3138,11117,11118,11120,11122],{"class":3136,"line":4165},[3138,11119,5374],{"class":3832},[3138,11121,3852],{"class":3836},[3138,11123,5379],{"class":3855},[3138,11125,11126,11128,11130],{"class":3136,"line":4178},[3138,11127,5384],{"class":3832},[3138,11129,3852],{"class":3836},[3138,11131,5390],{"class":5389},[3138,11133,11134],{"class":3136,"line":4189},[3138,11135,3772],{"emptyLinePlaceholder":3771},[3138,11137,11138,11140],{"class":3136,"line":4377},[3138,11139,4590],{"class":3832},[3138,11141,3837],{"class":3836},[3138,11143,11144,11146,11148],{"class":3136,"line":4385},[3138,11145,3849],{"class":3832},[3138,11147,3852],{"class":3836},[3138,11149,10232],{"class":3855},[3138,11151,11152,11154],{"class":3136,"line":4393},[3138,11153,4526],{"class":3832},[3138,11155,3837],{"class":3836},[3138,11157,11158,11160],{"class":3136,"line":4401},[3138,11159,6971],{"class":3832},[3138,11161,3837],{"class":3836},[3138,11163,11164,11166,11168],{"class":3136,"line":4406},[3138,11165,5273],{"class":3832},[3138,11167,3852],{"class":3836},[3138,11169,5278],{"class":3855},[3114,11171,11172,11173,11175,11176,3394],{},"Тепер Compose почекає, поки ",[3335,11174,4605],{}," пройде health check, перед запуском ",[3335,11177,4701],{},[3114,11179,11180],{},[3120,11181,11182],{},"Рішення 3: wait-for-it.sh (legacy)",[3114,11184,11185,11186,11189],{},"Використовуйте скрипт ",[3335,11187,11188],{},"wait-for-it.sh"," для очікування доступності порту:",[3670,11191,11193],{"className":3823,"code":11192,"language":3825,"meta":3675,"style":3675},"backend:\n  image: myapp-api\n  command: [\"./wait-for-it.sh\", \"db:5432\", \"--\", \"npm\", \"start\"]\n",[3335,11194,11195,11201,11209],{"__ignoreMap":3675},[3138,11196,11197,11199],{"class":3136,"line":3680},[3138,11198,4701],{"class":3832},[3138,11200,3837],{"class":3836},[3138,11202,11203,11205,11207],{"class":3136,"line":3694},[3138,11204,4622],{"class":3832},[3138,11206,3852],{"class":3836},[3138,11208,10232],{"class":3855},[3138,11210,11211,11214,11216,11219,11221,11224,11226,11229,11231,11233,11235,11238],{"class":3136,"line":3755},[3138,11212,11213],{"class":3832},"  command",[3138,11215,5338],{"class":3836},[3138,11217,11218],{"class":3687},"\"./wait-for-it.sh\"",[3138,11220,4272],{"class":3836},[3138,11222,11223],{"class":3687},"\"db:5432\"",[3138,11225,4272],{"class":3836},[3138,11227,11228],{"class":3687},"\"--\"",[3138,11230,4272],{"class":3836},[3138,11232,8241],{"class":3687},[3138,11234,4272],{"class":3836},[3138,11236,11237],{"class":3687},"\"start\"",[3138,11239,5359],{"class":3836},[3641,11241,11242,11244],{},[3120,11243,3645],{}," Використовуйте health checks для критичних залежностей (бази даних, черги повідомлень). Для некритичних — retry logic у коді.",[3114,11246,11247],{},[3398,11248],{"alt":11249,"className":11250,"src":11251},"Порівняння depends_on без очікування готовності і depends_on з healthcheck та condition service_healthy",[3402],"/images/tools/docker/docker-compose-basics/04.png",[3423,11253,11255],{"id":11254},"restart-policy-автоматичний-перезапуск","Restart Policy: автоматичний перезапуск",[3670,11257,11259],{"className":3823,"code":11258,"language":3825,"meta":3675,"style":3675},"services:\n  web:\n    image: nginx\n    restart: always\n\n  backend:\n    image: myapp-api\n    restart: on-failure\n\n  worker:\n    image: myapp-worker\n    restart: unless-stopped\n",[3335,11260,11261,11267,11273,11281,11290,11294,11300,11308,11316,11320,11327,11336],{"__ignoreMap":3675},[3138,11262,11263,11265],{"class":3136,"line":3680},[3138,11264,3833],{"class":3832},[3138,11266,3837],{"class":3836},[3138,11268,11269,11271],{"class":3136,"line":3694},[3138,11270,3842],{"class":3832},[3138,11272,3837],{"class":3836},[3138,11274,11275,11277,11279],{"class":3136,"line":3755},[3138,11276,3849],{"class":3832},[3138,11278,3852],{"class":3836},[3138,11280,9893],{"class":3855},[3138,11282,11283,11285,11287],{"class":3136,"line":3768},[3138,11284,6986],{"class":3832},[3138,11286,3852],{"class":3836},[3138,11288,11289],{"class":3855},"always\n",[3138,11291,11292],{"class":3136,"line":3775},[3138,11293,3772],{"emptyLinePlaceholder":3771},[3138,11295,11296,11298],{"class":3136,"line":3781},[3138,11297,4590],{"class":3832},[3138,11299,3837],{"class":3836},[3138,11301,11302,11304,11306],{"class":3136,"line":4165},[3138,11303,3849],{"class":3832},[3138,11305,3852],{"class":3836},[3138,11307,10232],{"class":3855},[3138,11309,11310,11312,11314],{"class":3136,"line":4178},[3138,11311,6986],{"class":3832},[3138,11313,3852],{"class":3836},[3138,11315,6991],{"class":3855},[3138,11317,11318],{"class":3136,"line":4189},[3138,11319,3772],{"emptyLinePlaceholder":3771},[3138,11321,11322,11325],{"class":3136,"line":4377},[3138,11323,11324],{"class":3832},"  worker",[3138,11326,3837],{"class":3836},[3138,11328,11329,11331,11333],{"class":3136,"line":4385},[3138,11330,3849],{"class":3832},[3138,11332,3852],{"class":3836},[3138,11334,11335],{"class":3855},"myapp-worker\n",[3138,11337,11338,11340,11342],{"class":3136,"line":4393},[3138,11339,6986],{"class":3832},[3138,11341,3852],{"class":3836},[3138,11343,11344],{"class":3855},"unless-stopped\n",[3114,11346,11347],{},[3120,11348,11349],{},"Опції:",[4204,11351,11352,11359,11366,11370],{},[4207,11353,11355,11358],{"name":11354,"type":5591},"no",[10145,11356,11357],{},"(За замовчуванням)"," Не перезапускати контейнер за жодних обставин.",[4207,11360,11362,11363,11365],{"name":11361,"type":5591},"always","Завжди перезапускати контейнер, якщо він зупинився. Якщо контейнер було зупинено вручну (через ",[3335,11364,9071],{},"), він все одно буде перезапущений після рестарту Docker daemon. Використовуйте для критичних сервісів (веб-сервер, API).",[4207,11367,11369],{"name":11368,"type":5591},"on-failure","Перезапускати лише якщо контейнер завершив роботу з помилкою (exit code != 0). Ідеально для worker-процесів або бекендів, що можуть тимчасово падати через нестачу пам'яті.",[4207,11371,11373],{"name":11372,"type":5591},"unless-stopped","Перезапускати завжди, окрім випадків, коли контейнер було зупинено вручну. Це найпопулярніший вибір для більшості сервісів, які мають працювати постійно.",[3416,11375],{},[3109,11377,11379],{"id":11378},"команди-docker-compose","Команди Docker Compose",[3423,11381,11383],{"id":11382},"основні-команди-життєвого-циклу","Основні команди життєвого циклу",[3114,11385,11386],{},[3120,11387,11388,11389,11391],{},"1. ",[3335,11390,3393],{}," — запустити застосунок",[3670,11393,11395],{"className":3672,"code":11394,"language":3674,"meta":3675,"style":3675},"# Запустити у foreground (логи у терміналі)\ndocker compose up\n\n# Запустити у background (detached mode)\ndocker compose up -d\n\n# Пересобрати образи перед запуском\ndocker compose up --build\n\n# Запустити лише конкретні сервіси\ndocker compose up backend db\n",[3335,11396,11397,11402,11410,11414,11419,11429,11433,11438,11448,11452,11457],{"__ignoreMap":3675},[3138,11398,11399],{"class":3136,"line":3680},[3138,11400,11401],{"class":3697},"# Запустити у foreground (логи у терміналі)\n",[3138,11403,11404,11406,11408],{"class":3136,"line":3694},[3138,11405,3684],{"class":3683},[3138,11407,3688],{"class":3687},[3138,11409,3890],{"class":3687},[3138,11411,11412],{"class":3136,"line":3755},[3138,11413,3772],{"emptyLinePlaceholder":3771},[3138,11415,11416],{"class":3136,"line":3768},[3138,11417,11418],{"class":3697},"# Запустити у background (detached mode)\n",[3138,11420,11421,11423,11425,11427],{"class":3136,"line":3775},[3138,11422,3684],{"class":3683},[3138,11424,3688],{"class":3687},[3138,11426,4919],{"class":3687},[3138,11428,4922],{"class":3725},[3138,11430,11431],{"class":3136,"line":3781},[3138,11432,3772],{"emptyLinePlaceholder":3771},[3138,11434,11435],{"class":3136,"line":4165},[3138,11436,11437],{"class":3697},"# Пересобрати образи перед запуском\n",[3138,11439,11440,11442,11444,11446],{"class":3136,"line":4178},[3138,11441,3684],{"class":3683},[3138,11443,3688],{"class":3687},[3138,11445,4919],{"class":3687},[3138,11447,9362],{"class":3725},[3138,11449,11450],{"class":3136,"line":4189},[3138,11451,3772],{"emptyLinePlaceholder":3771},[3138,11453,11454],{"class":3136,"line":4377},[3138,11455,11456],{"class":3697},"# Запустити лише конкретні сервіси\n",[3138,11458,11459,11461,11463,11465,11468],{"class":3136,"line":4385},[3138,11460,3684],{"class":3683},[3138,11462,3688],{"class":3687},[3138,11464,4919],{"class":3687},[3138,11466,11467],{"class":3687}," backend",[3138,11469,11470],{"class":3687}," db\n",[3114,11472,11473],{},[3120,11474,11475,11476,11478],{},"2. ",[3335,11477,3477],{}," — зупинити та видалити",[3670,11480,11482],{"className":3672,"code":11481,"language":3674,"meta":3675,"style":3675},"# Зупинити контейнери, видалити контейнери та мережі\ndocker compose down\n\n# Також видалити volumes (УВАГА: втрата даних)\ndocker compose down -v\n\n# Також видалити образи\ndocker compose down --rmi all\n",[3335,11483,11484,11489,11497,11501,11506,11518,11522,11527],{"__ignoreMap":3675},[3138,11485,11486],{"class":3136,"line":3680},[3138,11487,11488],{"class":3697},"# Зупинити контейнери, видалити контейнери та мережі\n",[3138,11490,11491,11493,11495],{"class":3136,"line":3694},[3138,11492,3684],{"class":3683},[3138,11494,3688],{"class":3687},[3138,11496,4061],{"class":3687},[3138,11498,11499],{"class":3136,"line":3755},[3138,11500,3772],{"emptyLinePlaceholder":3771},[3138,11502,11503],{"class":3136,"line":3768},[3138,11504,11505],{"class":3697},"# Також видалити volumes (УВАГА: втрата даних)\n",[3138,11507,11508,11510,11512,11515],{"class":3136,"line":3775},[3138,11509,3684],{"class":3683},[3138,11511,3688],{"class":3687},[3138,11513,11514],{"class":3687}," down",[3138,11516,11517],{"class":3725}," -v\n",[3138,11519,11520],{"class":3136,"line":3781},[3138,11521,3772],{"emptyLinePlaceholder":3771},[3138,11523,11524],{"class":3136,"line":4165},[3138,11525,11526],{"class":3697},"# Також видалити образи\n",[3138,11528,11529,11531,11533,11535,11538],{"class":3136,"line":4178},[3138,11530,3684],{"class":3683},[3138,11532,3688],{"class":3687},[3138,11534,11514],{"class":3687},[3138,11536,11537],{"class":3725}," --rmi",[3138,11539,11540],{"class":3687}," all\n",[3114,11542,11543],{},[3120,11544,11545,11546,11549],{},"3. ",[3335,11547,11548],{},"docker compose start/stop/restart"," — керування без видалення",[3670,11551,11553],{"className":3672,"code":11552,"language":3674,"meta":3675,"style":3675},"# Зупинити контейнери (не видаляти)\ndocker compose stop\n\n# Запустити зупинені контейнери\ndocker compose start\n\n# Перезапустити контейнери\ndocker compose restart\n\n# Перезапустити конкретний сервіс\ndocker compose restart backend\n",[3335,11554,11555,11560,11569,11573,11578,11587,11591,11596,11605,11609,11614],{"__ignoreMap":3675},[3138,11556,11557],{"class":3136,"line":3680},[3138,11558,11559],{"class":3697},"# Зупинити контейнери (не видаляти)\n",[3138,11561,11562,11564,11566],{"class":3136,"line":3694},[3138,11563,3684],{"class":3683},[3138,11565,3688],{"class":3687},[3138,11567,11568],{"class":3687}," stop\n",[3138,11570,11571],{"class":3136,"line":3755},[3138,11572,3772],{"emptyLinePlaceholder":3771},[3138,11574,11575],{"class":3136,"line":3768},[3138,11576,11577],{"class":3697},"# Запустити зупинені контейнери\n",[3138,11579,11580,11582,11584],{"class":3136,"line":3775},[3138,11581,3684],{"class":3683},[3138,11583,3688],{"class":3687},[3138,11585,11586],{"class":3687}," start\n",[3138,11588,11589],{"class":3136,"line":3781},[3138,11590,3772],{"emptyLinePlaceholder":3771},[3138,11592,11593],{"class":3136,"line":4165},[3138,11594,11595],{"class":3697},"# Перезапустити контейнери\n",[3138,11597,11598,11600,11602],{"class":3136,"line":4178},[3138,11599,3684],{"class":3683},[3138,11601,3688],{"class":3687},[3138,11603,11604],{"class":3687}," restart\n",[3138,11606,11607],{"class":3136,"line":4189},[3138,11608,3772],{"emptyLinePlaceholder":3771},[3138,11610,11611],{"class":3136,"line":4377},[3138,11612,11613],{"class":3697},"# Перезапустити конкретний сервіс\n",[3138,11615,11616,11618,11620,11623],{"class":3136,"line":4385},[3138,11617,3684],{"class":3683},[3138,11619,3688],{"class":3687},[3138,11621,11622],{"class":3687}," restart",[3138,11624,9400],{"class":3687},[3423,11626,11628],{"id":11627},"моніторинг-та-діагностика","Моніторинг та діагностика",[3114,11630,11631],{},[3120,11632,11633,11634,11637],{},"4. ",[3335,11635,11636],{},"docker compose ps"," — список сервісів",[3670,11639,11641],{"className":3672,"code":11640,"language":3674,"meta":3675,"style":3675},"docker compose ps\n\n# Вивід:\n# NAME                IMAGE           STATUS          PORTS\n# myapp-backend-1     myapp-api       Up 2 minutes    0.0.0.0:5000->5000/tcp\n# myapp-db-1          postgres:16     Up 2 minutes    5432/tcp\n",[3335,11642,11643,11651,11655,11660,11665,11670],{"__ignoreMap":3675},[3138,11644,11645,11647,11649],{"class":3136,"line":3680},[3138,11646,3684],{"class":3683},[3138,11648,3688],{"class":3687},[3138,11650,4037],{"class":3687},[3138,11652,11653],{"class":3136,"line":3694},[3138,11654,3772],{"emptyLinePlaceholder":3771},[3138,11656,11657],{"class":3136,"line":3755},[3138,11658,11659],{"class":3697},"# Вивід:\n",[3138,11661,11662],{"class":3136,"line":3768},[3138,11663,11664],{"class":3697},"# NAME                IMAGE           STATUS          PORTS\n",[3138,11666,11667],{"class":3136,"line":3775},[3138,11668,11669],{"class":3697},"# myapp-backend-1     myapp-api       Up 2 minutes    0.0.0.0:5000->5000/tcp\n",[3138,11671,11672],{"class":3136,"line":3781},[3138,11673,11674],{"class":3697},"# myapp-db-1          postgres:16     Up 2 minutes    5432/tcp\n",[3114,11676,11677],{},[3120,11678,11679,11680,11683],{},"5. ",[3335,11681,11682],{},"docker compose logs"," — перегляд логів",[3670,11685,11687],{"className":3672,"code":11686,"language":3674,"meta":3675,"style":3675},"# Логи всіх сервісів\ndocker compose logs\n\n# Логи конкретного сервісу\ndocker compose logs backend\n\n# Слідкувати за логами в реальному часі\ndocker compose logs -f\n\n# Останні 100 рядків\ndocker compose logs --tail=100\n\n# Логи з timestamps\ndocker compose logs -t\n",[3335,11688,11689,11694,11702,11706,11711,11721,11725,11729,11739,11743,11748,11759,11763,11768],{"__ignoreMap":3675},[3138,11690,11691],{"class":3136,"line":3680},[3138,11692,11693],{"class":3697},"# Логи всіх сервісів\n",[3138,11695,11696,11698,11700],{"class":3136,"line":3694},[3138,11697,3684],{"class":3683},[3138,11699,3688],{"class":3687},[3138,11701,5030],{"class":3687},[3138,11703,11704],{"class":3136,"line":3755},[3138,11705,3772],{"emptyLinePlaceholder":3771},[3138,11707,11708],{"class":3136,"line":3768},[3138,11709,11710],{"class":3697},"# Логи конкретного сервісу\n",[3138,11712,11713,11715,11717,11719],{"class":3136,"line":3775},[3138,11714,3684],{"class":3683},[3138,11716,3688],{"class":3687},[3138,11718,5048],{"class":3687},[3138,11720,9400],{"class":3687},[3138,11722,11723],{"class":3136,"line":3781},[3138,11724,3772],{"emptyLinePlaceholder":3771},[3138,11726,11727],{"class":3136,"line":4165},[3138,11728,5060],{"class":3697},[3138,11730,11731,11733,11735,11737],{"class":3136,"line":4178},[3138,11732,3684],{"class":3683},[3138,11734,3688],{"class":3687},[3138,11736,5048],{"class":3687},[3138,11738,5071],{"class":3725},[3138,11740,11741],{"class":3136,"line":4189},[3138,11742,3772],{"emptyLinePlaceholder":3771},[3138,11744,11745],{"class":3136,"line":4377},[3138,11746,11747],{"class":3697},"# Останні 100 рядків\n",[3138,11749,11750,11752,11754,11756],{"class":3136,"line":4385},[3138,11751,3684],{"class":3683},[3138,11753,3688],{"class":3687},[3138,11755,5048],{"class":3687},[3138,11757,11758],{"class":3725}," --tail=100\n",[3138,11760,11761],{"class":3136,"line":4393},[3138,11762,3772],{"emptyLinePlaceholder":3771},[3138,11764,11765],{"class":3136,"line":4401},[3138,11766,11767],{"class":3697},"# Логи з timestamps\n",[3138,11769,11770,11772,11774,11776],{"class":3136,"line":4406},[3138,11771,3684],{"class":3683},[3138,11773,3688],{"class":3687},[3138,11775,5048],{"class":3687},[3138,11777,11778],{"class":3725}," -t\n",[3114,11780,11781],{},[3120,11782,11783,11784,11787],{},"6. ",[3335,11785,11786],{},"docker compose exec"," — виконати команду у контейнері",[3670,11789,11791],{"className":3672,"code":11790,"language":3674,"meta":3675,"style":3675},"# Відкрити shell у контейнері backend\ndocker compose exec backend sh\n\n# Виконати команду без інтерактивного режиму\ndocker compose exec backend npm run migrate\n\n# Виконати як root\ndocker compose exec -u root backend apt-get update\n",[3335,11792,11793,11798,11812,11816,11821,11840,11844,11849],{"__ignoreMap":3675},[3138,11794,11795],{"class":3136,"line":3680},[3138,11796,11797],{"class":3697},"# Відкрити shell у контейнері backend\n",[3138,11799,11800,11802,11804,11807,11809],{"class":3136,"line":3694},[3138,11801,3684],{"class":3683},[3138,11803,3688],{"class":3687},[3138,11805,11806],{"class":3687}," exec",[3138,11808,11467],{"class":3687},[3138,11810,11811],{"class":3687}," sh\n",[3138,11813,11814],{"class":3136,"line":3755},[3138,11815,3772],{"emptyLinePlaceholder":3771},[3138,11817,11818],{"class":3136,"line":3768},[3138,11819,11820],{"class":3697},"# Виконати команду без інтерактивного режиму\n",[3138,11822,11823,11825,11827,11829,11831,11834,11837],{"class":3136,"line":3775},[3138,11824,3684],{"class":3683},[3138,11826,3688],{"class":3687},[3138,11828,11806],{"class":3687},[3138,11830,11467],{"class":3687},[3138,11832,11833],{"class":3687}," npm",[3138,11835,11836],{"class":3687}," run",[3138,11838,11839],{"class":3687}," migrate\n",[3138,11841,11842],{"class":3136,"line":3781},[3138,11843,3772],{"emptyLinePlaceholder":3771},[3138,11845,11846],{"class":3136,"line":4165},[3138,11847,11848],{"class":3697},"# Виконати як root\n",[3138,11850,11851,11853,11855,11857,11860,11863,11865,11868],{"class":3136,"line":4178},[3138,11852,3684],{"class":3683},[3138,11854,3688],{"class":3687},[3138,11856,11806],{"class":3687},[3138,11858,11859],{"class":3725}," -u",[3138,11861,11862],{"class":3687}," root",[3138,11864,11467],{"class":3687},[3138,11866,11867],{"class":3687}," apt-get",[3138,11869,11870],{"class":3687}," update\n",[3114,11872,11873],{},[3120,11874,11875,11876,11879],{},"7. ",[3335,11877,11878],{},"docker compose top"," — процеси у контейнерах",[3670,11881,11883],{"className":3672,"code":11882,"language":3674,"meta":3675,"style":3675},"docker compose top\n\n# Показує PID, USER, TIME, COMMAND для кожного сервісу\n",[3335,11884,11885,11894,11898],{"__ignoreMap":3675},[3138,11886,11887,11889,11891],{"class":3136,"line":3680},[3138,11888,3684],{"class":3683},[3138,11890,3688],{"class":3687},[3138,11892,11893],{"class":3687}," top\n",[3138,11895,11896],{"class":3136,"line":3694},[3138,11897,3772],{"emptyLinePlaceholder":3771},[3138,11899,11900],{"class":3136,"line":3755},[3138,11901,11902],{"class":3697},"# Показує PID, USER, TIME, COMMAND для кожного сервісу\n",[3423,11904,11906],{"id":11905},"управління-образами-та-volumes","Управління образами та volumes",[3114,11908,11909],{},[3120,11910,11911,11912,11915],{},"8. ",[3335,11913,11914],{},"docker compose build"," — збірка образів",[3670,11917,11919],{"className":3672,"code":11918,"language":3674,"meta":3675,"style":3675},"# Зібрати всі сервіси з build-секцією\ndocker compose build\n\n# Зібрати конкретний сервіс\ndocker compose build backend\n\n# Зібрати без кешу\ndocker compose build --no-cache\n\n# Паралельна збірка\ndocker compose build --parallel\n",[3335,11920,11921,11926,11934,11938,11942,11952,11956,11961,11972,11976,11981],{"__ignoreMap":3675},[3138,11922,11923],{"class":3136,"line":3680},[3138,11924,11925],{"class":3697},"# Зібрати всі сервіси з build-секцією\n",[3138,11927,11928,11930,11932],{"class":3136,"line":3694},[3138,11929,3684],{"class":3683},[3138,11931,3688],{"class":3687},[3138,11933,6209],{"class":3687},[3138,11935,11936],{"class":3136,"line":3755},[3138,11937,3772],{"emptyLinePlaceholder":3771},[3138,11939,11940],{"class":3136,"line":3768},[3138,11941,9388],{"class":3697},[3138,11943,11944,11946,11948,11950],{"class":3136,"line":3775},[3138,11945,3684],{"class":3683},[3138,11947,3688],{"class":3687},[3138,11949,9397],{"class":3687},[3138,11951,9400],{"class":3687},[3138,11953,11954],{"class":3136,"line":3781},[3138,11955,3772],{"emptyLinePlaceholder":3771},[3138,11957,11958],{"class":3136,"line":4165},[3138,11959,11960],{"class":3697},"# Зібрати без кешу\n",[3138,11962,11963,11965,11967,11969],{"class":3136,"line":4178},[3138,11964,3684],{"class":3683},[3138,11966,3688],{"class":3687},[3138,11968,9397],{"class":3687},[3138,11970,11971],{"class":3725}," --no-cache\n",[3138,11973,11974],{"class":3136,"line":4189},[3138,11975,3772],{"emptyLinePlaceholder":3771},[3138,11977,11978],{"class":3136,"line":4377},[3138,11979,11980],{"class":3697},"# Паралельна збірка\n",[3138,11982,11983,11985,11987,11989],{"class":3136,"line":4385},[3138,11984,3684],{"class":3683},[3138,11986,3688],{"class":3687},[3138,11988,9397],{"class":3687},[3138,11990,11991],{"class":3725}," --parallel\n",[3114,11993,11994],{},[3120,11995,11996,11997,12000],{},"9. ",[3335,11998,11999],{},"docker compose pull"," — завантажити образи",[3670,12002,12004],{"className":3672,"code":12003,"language":3674,"meta":3675,"style":3675},"# Завантажити всі образи з registry\ndocker compose pull\n\n# Завантажити конкретний сервіс\ndocker compose pull db\n",[3335,12005,12006,12011,12020,12024,12029],{"__ignoreMap":3675},[3138,12007,12008],{"class":3136,"line":3680},[3138,12009,12010],{"class":3697},"# Завантажити всі образи з registry\n",[3138,12012,12013,12015,12017],{"class":3136,"line":3694},[3138,12014,3684],{"class":3683},[3138,12016,3688],{"class":3687},[3138,12018,12019],{"class":3687}," pull\n",[3138,12021,12022],{"class":3136,"line":3755},[3138,12023,3772],{"emptyLinePlaceholder":3771},[3138,12025,12026],{"class":3136,"line":3768},[3138,12027,12028],{"class":3697},"# Завантажити конкретний сервіс\n",[3138,12030,12031,12033,12035,12038],{"class":3136,"line":3775},[3138,12032,3684],{"class":3683},[3138,12034,3688],{"class":3687},[3138,12036,12037],{"class":3687}," pull",[3138,12039,11470],{"class":3687},[3114,12041,12042],{},[3120,12043,12044,12045,12048],{},"10. ",[3335,12046,12047],{},"docker compose push"," — відправити образи до registry",[3670,12050,12052],{"className":3672,"code":12051,"language":3674,"meta":3675,"style":3675},"# Push всіх образів з image-секцією\ndocker compose push\n\n# Push конкретного сервісу\ndocker compose push backend\n",[3335,12053,12054,12059,12068,12072,12077],{"__ignoreMap":3675},[3138,12055,12056],{"class":3136,"line":3680},[3138,12057,12058],{"class":3697},"# Push всіх образів з image-секцією\n",[3138,12060,12061,12063,12065],{"class":3136,"line":3694},[3138,12062,3684],{"class":3683},[3138,12064,3688],{"class":3687},[3138,12066,12067],{"class":3687}," push\n",[3138,12069,12070],{"class":3136,"line":3755},[3138,12071,3772],{"emptyLinePlaceholder":3771},[3138,12073,12074],{"class":3136,"line":3768},[3138,12075,12076],{"class":3697},"# Push конкретного сервісу\n",[3138,12078,12079,12081,12083,12086],{"class":3136,"line":3775},[3138,12080,3684],{"class":3683},[3138,12082,3688],{"class":3687},[3138,12084,12085],{"class":3687}," push",[3138,12087,9400],{"class":3687},[3423,12089,3556],{"id":12090},"масштабування",[3114,12092,12093],{},[3120,12094,12095,12096,12099],{},"11. ",[3335,12097,12098],{},"docker compose up --scale"," — масштабування сервісів",[3670,12101,12103],{"className":3672,"code":12102,"language":3674,"meta":3675,"style":3675},"# Запустити 3 репліки backend\ndocker compose up -d --scale backend=3\n\n# Масштабувати кілька сервісів\ndocker compose up -d --scale backend=3 --scale worker=5\n",[3335,12104,12105,12110,12129,12133,12138],{"__ignoreMap":3675},[3138,12106,12107],{"class":3136,"line":3680},[3138,12108,12109],{"class":3697},"# Запустити 3 репліки backend\n",[3138,12111,12112,12114,12116,12118,12120,12123,12126],{"class":3136,"line":3694},[3138,12113,3684],{"class":3683},[3138,12115,3688],{"class":3687},[3138,12117,4919],{"class":3687},[3138,12119,6115],{"class":3725},[3138,12121,12122],{"class":3725}," --scale",[3138,12124,12125],{"class":3687}," backend=",[3138,12127,12128],{"class":5389},"3\n",[3138,12130,12131],{"class":3136,"line":3755},[3138,12132,3772],{"emptyLinePlaceholder":3771},[3138,12134,12135],{"class":3136,"line":3768},[3138,12136,12137],{"class":3697},"# Масштабувати кілька сервісів\n",[3138,12139,12140,12142,12144,12146,12148,12150,12152,12155,12157,12160],{"class":3136,"line":3775},[3138,12141,3684],{"class":3683},[3138,12143,3688],{"class":3687},[3138,12145,4919],{"class":3687},[3138,12147,6115],{"class":3725},[3138,12149,12122],{"class":3725},[3138,12151,12125],{"class":3687},[3138,12153,12154],{"class":5389},"3",[3138,12156,12122],{"class":3725},[3138,12158,12159],{"class":3687}," worker=",[3138,12161,5390],{"class":5389},[3114,12163,12164,12167],{},[3120,12165,12166],{},"Обмеження:"," Не можна масштабувати сервіси з пробросом портів (конфлікт портів). Рішення — використовувати load balancer або не вказувати конкретний порт хоста.",[3114,12169,12170],{},[3120,12171,12172],{},"Приклад для масштабування:",[3670,12174,12176],{"className":3823,"code":12175,"language":3825,"meta":3675,"style":3675},"services:\n  backend:\n    image: myapp-api\n    # Не вказувати ports для масштабування\n    expose:\n      - \"5000\"\n\n  nginx:\n    image: nginx\n    ports:\n      - \"80:80\"\n    # Nginx як load balancer для backend\n",[3335,12177,12178,12184,12190,12198,12203,12209,12216,12220,12227,12235,12241,12248],{"__ignoreMap":3675},[3138,12179,12180,12182],{"class":3136,"line":3680},[3138,12181,3833],{"class":3832},[3138,12183,3837],{"class":3836},[3138,12185,12186,12188],{"class":3136,"line":3694},[3138,12187,4590],{"class":3832},[3138,12189,3837],{"class":3836},[3138,12191,12192,12194,12196],{"class":3136,"line":3755},[3138,12193,3849],{"class":3832},[3138,12195,3852],{"class":3836},[3138,12197,10232],{"class":3855},[3138,12199,12200],{"class":3136,"line":3768},[3138,12201,12202],{"class":3697},"    # Не вказувати ports для масштабування\n",[3138,12204,12205,12207],{"class":3136,"line":3775},[3138,12206,10730],{"class":3832},[3138,12208,3837],{"class":3836},[3138,12210,12211,12213],{"class":3136,"line":3781},[3138,12212,3868],{"class":3836},[3138,12214,12215],{"class":3687},"\"5000\"\n",[3138,12217,12218],{"class":3136,"line":4165},[3138,12219,3772],{"emptyLinePlaceholder":3771},[3138,12221,12222,12225],{"class":3136,"line":4178},[3138,12223,12224],{"class":3832},"  nginx",[3138,12226,3837],{"class":3836},[3138,12228,12229,12231,12233],{"class":3136,"line":4189},[3138,12230,3849],{"class":3832},[3138,12232,3852],{"class":3836},[3138,12234,9893],{"class":3855},[3138,12236,12237,12239],{"class":3136,"line":4377},[3138,12238,3861],{"class":3832},[3138,12240,3837],{"class":3836},[3138,12242,12243,12245],{"class":3136,"line":4385},[3138,12244,3868],{"class":3836},[3138,12246,12247],{"class":3687},"\"80:80\"\n",[3138,12249,12250],{"class":3136,"line":4393},[3138,12251,12252],{"class":3697},"    # Nginx як load balancer для backend\n",[3416,12254],{},[3109,12256,12258],{"id":12257},"просунуті-можливості-compose","Просунуті можливості Compose",[3423,12260,12262],{"id":12261},"extends-повторне-використання-конфігурації","Extends: повторне використання конфігурації",[3114,12264,12265,12268],{},[3120,12266,12267],{},"Проблема:"," У вас є спільна конфігурація для кількох сервісів (наприклад, logging, restart policy).",[3114,12270,12271,12273,12274,3394],{},[3120,12272,3377],{}," Винесіть спільну конфігурацію у базовий файл та розширюйте її за допомогою директиви ",[3335,12275,12276],{},"extends",[6186,12278,12279,12350],{},[3670,12280,12283],{"className":3823,"code":12281,"filename":12282,"language":3825,"meta":3675,"style":3675},"services:\n  base-service:\n    restart: unless-stopped\n    logging:\n      driver: json-file\n      options:\n        max-size: \"10m\"\n        max-file: \"3\"\n","docker-compose.base.yml",[3335,12284,12285,12291,12298,12306,12313,12323,12330,12340],{"__ignoreMap":3675},[3138,12286,12287,12289],{"class":3136,"line":3680},[3138,12288,3833],{"class":3832},[3138,12290,3837],{"class":3836},[3138,12292,12293,12296],{"class":3136,"line":3694},[3138,12294,12295],{"class":3832},"  base-service",[3138,12297,3837],{"class":3836},[3138,12299,12300,12302,12304],{"class":3136,"line":3755},[3138,12301,6986],{"class":3832},[3138,12303,3852],{"class":3836},[3138,12305,11344],{"class":3855},[3138,12307,12308,12311],{"class":3136,"line":3768},[3138,12309,12310],{"class":3832},"    logging",[3138,12312,3837],{"class":3836},[3138,12314,12315,12318,12320],{"class":3136,"line":3775},[3138,12316,12317],{"class":3832},"      driver",[3138,12319,3852],{"class":3836},[3138,12321,12322],{"class":3855},"json-file\n",[3138,12324,12325,12328],{"class":3136,"line":3781},[3138,12326,12327],{"class":3832},"      options",[3138,12329,3837],{"class":3836},[3138,12331,12332,12335,12337],{"class":3136,"line":4165},[3138,12333,12334],{"class":3832},"        max-size",[3138,12336,3852],{"class":3836},[3138,12338,12339],{"class":3687},"\"10m\"\n",[3138,12341,12342,12345,12347],{"class":3136,"line":4178},[3138,12343,12344],{"class":3832},"        max-file",[3138,12346,3852],{"class":3836},[3138,12348,12349],{"class":3687},"\"3\"\n",[3670,12351,12353],{"className":3823,"code":12352,"filename":3409,"language":3825,"meta":3675,"style":3675},"services:\n  backend:\n    extends:\n      file: docker-compose.base.yml\n      service: base-service\n    image: myapp-api\n    ports:\n      - \"5000:5000\"\n\n  frontend:\n    extends:\n      file: docker-compose.base.yml\n      service: base-service\n    image: myapp-frontend\n    ports:\n      - \"3000:3000\"\n",[3335,12354,12355,12361,12367,12374,12384,12394,12402,12408,12414,12418,12424,12430,12438,12446,12455,12461],{"__ignoreMap":3675},[3138,12356,12357,12359],{"class":3136,"line":3680},[3138,12358,3833],{"class":3832},[3138,12360,3837],{"class":3836},[3138,12362,12363,12365],{"class":3136,"line":3694},[3138,12364,4590],{"class":3832},[3138,12366,3837],{"class":3836},[3138,12368,12369,12372],{"class":3136,"line":3755},[3138,12370,12371],{"class":3832},"    extends",[3138,12373,3837],{"class":3836},[3138,12375,12376,12379,12381],{"class":3136,"line":3768},[3138,12377,12378],{"class":3832},"      file",[3138,12380,3852],{"class":3836},[3138,12382,12383],{"class":3855},"docker-compose.base.yml\n",[3138,12385,12386,12389,12391],{"class":3136,"line":3775},[3138,12387,12388],{"class":3832},"      service",[3138,12390,3852],{"class":3836},[3138,12392,12393],{"class":3855},"base-service\n",[3138,12395,12396,12398,12400],{"class":3136,"line":3781},[3138,12397,3849],{"class":3832},[3138,12399,3852],{"class":3836},[3138,12401,10232],{"class":3855},[3138,12403,12404,12406],{"class":3136,"line":4165},[3138,12405,3861],{"class":3832},[3138,12407,3837],{"class":3836},[3138,12409,12410,12412],{"class":3136,"line":4178},[3138,12411,3868],{"class":3836},[3138,12413,9245],{"class":3687},[3138,12415,12416],{"class":3136,"line":4189},[3138,12417,3772],{"emptyLinePlaceholder":3771},[3138,12419,12420,12422],{"class":3136,"line":4377},[3138,12421,4582],{"class":3832},[3138,12423,3837],{"class":3836},[3138,12425,12426,12428],{"class":3136,"line":4385},[3138,12427,12371],{"class":3832},[3138,12429,3837],{"class":3836},[3138,12431,12432,12434,12436],{"class":3136,"line":4393},[3138,12433,12378],{"class":3832},[3138,12435,3852],{"class":3836},[3138,12437,12383],{"class":3855},[3138,12439,12440,12442,12444],{"class":3136,"line":4401},[3138,12441,12388],{"class":3832},[3138,12443,3852],{"class":3836},[3138,12445,12393],{"class":3855},[3138,12447,12448,12450,12452],{"class":3136,"line":4406},[3138,12449,3849],{"class":3832},[3138,12451,3852],{"class":3836},[3138,12453,12454],{"class":3855},"myapp-frontend\n",[3138,12456,12457,12459],{"class":3136,"line":4414},[3138,12458,3861],{"class":3832},[3138,12460,3837],{"class":3836},[3138,12462,12463,12465],{"class":3136,"line":4424},[3138,12464,3868],{"class":3836},[3138,12466,5217],{"class":3687},[3114,12468,12469,12470,4848,12473,12476,12477,3394],{},"Обидва сервіси успадкують ",[3335,12471,12472],{},"restart",[3335,12474,12475],{},"logging"," з ",[3335,12478,12479],{},"base-service",[3423,12481,12483],{"id":12482},"profiles-умовний-запуск-сервісів","Profiles: умовний запуск сервісів",[3114,12485,12486,12489],{},[3120,12487,12488],{},"Сценарій:"," У вас є основні сервіси (backend, db) та допоміжні (adminer для перегляду БД, mailhog для тестування email). Допоміжні потрібні не завжди.",[3114,12491,12492,12494],{},[3120,12493,3377],{}," Використовуйте profiles.",[3114,12496,12497],{},[3120,12498,4293],{},[3670,12500,12502],{"className":3823,"code":12501,"language":3825,"meta":3675,"style":3675},"services:\n  backend:\n    image: myapp-api\n    # Немає profile — запускається завжди\n\n  db:\n    image: postgres:16\n    # Немає profile — запускається завжди\n\n  adminer:\n    image: adminer\n    ports:\n      - \"8080:8080\"\n    profiles:\n      - debug\n\n  mailhog:\n    image: mailhog/mailhog\n    ports:\n      - \"8025:8025\"\n    profiles:\n      - debug\n",[3335,12503,12504,12510,12516,12524,12529,12533,12539,12547,12551,12555,12562,12571,12577,12584,12590,12596,12600,12607,12616,12622,12629,12635],{"__ignoreMap":3675},[3138,12505,12506,12508],{"class":3136,"line":3680},[3138,12507,3833],{"class":3832},[3138,12509,3837],{"class":3836},[3138,12511,12512,12514],{"class":3136,"line":3694},[3138,12513,4590],{"class":3832},[3138,12515,3837],{"class":3836},[3138,12517,12518,12520,12522],{"class":3136,"line":3755},[3138,12519,3849],{"class":3832},[3138,12521,3852],{"class":3836},[3138,12523,10232],{"class":3855},[3138,12525,12526],{"class":3136,"line":3768},[3138,12527,12528],{"class":3697},"    # Немає profile — запускається завжди\n",[3138,12530,12531],{"class":3136,"line":3775},[3138,12532,3772],{"emptyLinePlaceholder":3771},[3138,12534,12535,12537],{"class":3136,"line":3781},[3138,12536,4309],{"class":3832},[3138,12538,3837],{"class":3836},[3138,12540,12541,12543,12545],{"class":3136,"line":4165},[3138,12542,3849],{"class":3832},[3138,12544,3852],{"class":3836},[3138,12546,9829],{"class":3855},[3138,12548,12549],{"class":3136,"line":4178},[3138,12550,12528],{"class":3697},[3138,12552,12553],{"class":3136,"line":4189},[3138,12554,3772],{"emptyLinePlaceholder":3771},[3138,12556,12557,12560],{"class":3136,"line":4377},[3138,12558,12559],{"class":3832},"  adminer",[3138,12561,3837],{"class":3836},[3138,12563,12564,12566,12568],{"class":3136,"line":4385},[3138,12565,3849],{"class":3832},[3138,12567,3852],{"class":3836},[3138,12569,12570],{"class":3855},"adminer\n",[3138,12572,12573,12575],{"class":3136,"line":4393},[3138,12574,3861],{"class":3832},[3138,12576,3837],{"class":3836},[3138,12578,12579,12581],{"class":3136,"line":4401},[3138,12580,3868],{"class":3836},[3138,12582,12583],{"class":3687},"\"8080:8080\"\n",[3138,12585,12586,12588],{"class":3136,"line":4406},[3138,12587,5503],{"class":3832},[3138,12589,3837],{"class":3836},[3138,12591,12592,12594],{"class":3136,"line":4414},[3138,12593,3868],{"class":3836},[3138,12595,5513],{"class":3855},[3138,12597,12598],{"class":3136,"line":4424},[3138,12599,3772],{"emptyLinePlaceholder":3771},[3138,12601,12602,12605],{"class":3136,"line":4431},[3138,12603,12604],{"class":3832},"  mailhog",[3138,12606,3837],{"class":3836},[3138,12608,12609,12611,12613],{"class":3136,"line":4438},[3138,12610,3849],{"class":3832},[3138,12612,3852],{"class":3836},[3138,12614,12615],{"class":3855},"mailhog/mailhog\n",[3138,12617,12618,12620],{"class":3136,"line":4445},[3138,12619,3861],{"class":3832},[3138,12621,3837],{"class":3836},[3138,12623,12624,12626],{"class":3136,"line":4456},[3138,12625,3868],{"class":3836},[3138,12627,12628],{"class":3687},"\"8025:8025\"\n",[3138,12630,12631,12633],{"class":3136,"line":4466},[3138,12632,5503],{"class":3832},[3138,12634,3837],{"class":3836},[3138,12636,12637,12639],{"class":3136,"line":4476},[3138,12638,3868],{"class":3836},[3138,12640,5513],{"class":3855},[3114,12642,12643],{},[3120,12644,12645],{},"Запуск:",[3670,12647,12649],{"className":3672,"code":12648,"language":3674,"meta":3675,"style":3675},"# Запустити лише основні сервіси (backend, db)\ndocker compose up -d\n\n# Запустити з debug-профілем (backend, db, adminer, mailhog)\ndocker compose --profile debug up -d\n\n# Запустити кілька профілів\ndocker compose --profile debug --profile monitoring up -d\n",[3335,12650,12651,12656,12666,12670,12675,12689,12693,12698],{"__ignoreMap":3675},[3138,12652,12653],{"class":3136,"line":3680},[3138,12654,12655],{"class":3697},"# Запустити лише основні сервіси (backend, db)\n",[3138,12657,12658,12660,12662,12664],{"class":3136,"line":3694},[3138,12659,3684],{"class":3683},[3138,12661,3688],{"class":3687},[3138,12663,4919],{"class":3687},[3138,12665,4922],{"class":3725},[3138,12667,12668],{"class":3136,"line":3755},[3138,12669,3772],{"emptyLinePlaceholder":3771},[3138,12671,12672],{"class":3136,"line":3768},[3138,12673,12674],{"class":3697},"# Запустити з debug-профілем (backend, db, adminer, mailhog)\n",[3138,12676,12677,12679,12681,12683,12685,12687],{"class":3136,"line":3775},[3138,12678,3684],{"class":3683},[3138,12680,3688],{"class":3687},[3138,12682,6107],{"class":3725},[3138,12684,6110],{"class":3687},[3138,12686,4919],{"class":3687},[3138,12688,4922],{"class":3725},[3138,12690,12691],{"class":3136,"line":3781},[3138,12692,3772],{"emptyLinePlaceholder":3771},[3138,12694,12695],{"class":3136,"line":4165},[3138,12696,12697],{"class":3697},"# Запустити кілька профілів\n",[3138,12699,12700,12702,12704,12706,12708,12710,12713,12715],{"class":3136,"line":4178},[3138,12701,3684],{"class":3683},[3138,12703,3688],{"class":3687},[3138,12705,6107],{"class":3725},[3138,12707,6110],{"class":3687},[3138,12709,6107],{"class":3725},[3138,12711,12712],{"class":3687}," monitoring",[3138,12714,4919],{"class":3687},[3138,12716,4922],{"class":3725},[3114,12718,12719],{},[3120,12720,12721],{},"Use cases:",[3325,12723,12724,12731,12739],{},[3328,12725,12726,12730],{},[3120,12727,12728],{},[3335,12729,5104],{}," — інструменти для розробки (Adminer, Mailhog, Redis Commander)",[3328,12732,12733,12738],{},[3120,12734,12735],{},[3335,12736,12737],{},"test"," — сервіси для integration tests",[3328,12740,12741,12746],{},[3120,12742,12743],{},[3335,12744,12745],{},"monitoring"," — Prometheus, Grafana",[3114,12748,12749],{},[3398,12750],{"alt":12751,"className":12752,"src":12753},"Базовий docker-compose.yml, debug profile, development override і production override як шари конфігурації",[3402],"/images/tools/docker/docker-compose-basics/05.png",[3423,12755,12757],{"id":12756},"override-files-різні-середовища","Override Files: різні середовища",[3114,12759,12760,12762],{},[3120,12761,12267],{}," Конфігурація для development відрізняється від production (різні порти, volumes для hot-reload, debug-інструменти).",[3114,12764,12765,12767],{},[3120,12766,3377],{}," Використовуйте override-файли.",[3114,12769,12770],{},[3120,12771,12772],{},"Базова конфігурація (docker-compose.yml):",[3670,12774,12776],{"className":3823,"code":12775,"language":3825,"meta":3675,"style":3675},"services:\n  backend:\n    image: myapp-api:latest\n    environment:\n      - NODE_ENV=production\n\n  db:\n    image: postgres:16\n    environment:\n      - POSTGRES_PASSWORD=changeme\n",[3335,12777,12778,12784,12790,12799,12805,12811,12815,12821,12829,12835],{"__ignoreMap":3675},[3138,12779,12780,12782],{"class":3136,"line":3680},[3138,12781,3833],{"class":3832},[3138,12783,3837],{"class":3836},[3138,12785,12786,12788],{"class":3136,"line":3694},[3138,12787,4590],{"class":3832},[3138,12789,3837],{"class":3836},[3138,12791,12792,12794,12796],{"class":3136,"line":3755},[3138,12793,3849],{"class":3832},[3138,12795,3852],{"class":3836},[3138,12797,12798],{"class":3855},"myapp-api:latest\n",[3138,12800,12801,12803],{"class":3136,"line":3768},[3138,12802,4325],{"class":3832},[3138,12804,3837],{"class":3836},[3138,12806,12807,12809],{"class":3136,"line":3775},[3138,12808,3868],{"class":3836},[3138,12810,9549],{"class":3855},[3138,12812,12813],{"class":3136,"line":3781},[3138,12814,3772],{"emptyLinePlaceholder":3771},[3138,12816,12817,12819],{"class":3136,"line":4165},[3138,12818,4309],{"class":3832},[3138,12820,3837],{"class":3836},[3138,12822,12823,12825,12827],{"class":3136,"line":4178},[3138,12824,3849],{"class":3832},[3138,12826,3852],{"class":3836},[3138,12828,9829],{"class":3855},[3138,12830,12831,12833],{"class":3136,"line":4189},[3138,12832,4325],{"class":3832},[3138,12834,3837],{"class":3836},[3138,12836,12837,12839],{"class":3136,"line":4377},[3138,12838,3868],{"class":3836},[3138,12840,12841],{"class":3855},"POSTGRES_PASSWORD=changeme\n",[3114,12843,12844],{},[3120,12845,12846],{},"Development override (docker-compose.override.yml):",[3670,12848,12850],{"className":3823,"code":12849,"language":3825,"meta":3675,"style":3675},"services:\n  backend:\n    build: ./backend  # Збирати локально замість image\n    volumes:\n      - ./backend/src:/app/src  # Hot-reload\n    environment:\n      - NODE_ENV=development\n    command: npm run dev\n\n  db:\n    ports:\n      - \"5432:5432\"  # Проброс для доступу з хоста\n",[3335,12851,12852,12858,12864,12876,12882,12891,12897,12903,12911,12915,12921,12927],{"__ignoreMap":3675},[3138,12853,12854,12856],{"class":3136,"line":3680},[3138,12855,3833],{"class":3832},[3138,12857,3837],{"class":3836},[3138,12859,12860,12862],{"class":3136,"line":3694},[3138,12861,4590],{"class":3832},[3138,12863,3837],{"class":3836},[3138,12865,12866,12868,12870,12873],{"class":3136,"line":3755},[3138,12867,6874],{"class":3832},[3138,12869,3852],{"class":3836},[3138,12871,12872],{"class":3855},"./backend",[3138,12874,12875],{"class":3697},"  # Збирати локально замість image\n",[3138,12877,12878,12880],{"class":3136,"line":3768},[3138,12879,4372],{"class":3832},[3138,12881,3837],{"class":3836},[3138,12883,12884,12886,12888],{"class":3136,"line":3775},[3138,12885,3868],{"class":3836},[3138,12887,10088],{"class":3855},[3138,12889,12890],{"class":3697},"  # Hot-reload\n",[3138,12892,12893,12895],{"class":3136,"line":3781},[3138,12894,4325],{"class":3832},[3138,12896,3837],{"class":3836},[3138,12898,12899,12901],{"class":3136,"line":4165},[3138,12900,3868],{"class":3836},[3138,12902,9232],{"class":3855},[3138,12904,12905,12907,12909],{"class":3136,"line":4178},[3138,12906,5199],{"class":3832},[3138,12908,3852],{"class":3836},[3138,12910,10109],{"class":3855},[3138,12912,12913],{"class":3136,"line":4189},[3138,12914,3772],{"emptyLinePlaceholder":3771},[3138,12916,12917,12919],{"class":3136,"line":4377},[3138,12918,4309],{"class":3832},[3138,12920,3837],{"class":3836},[3138,12922,12923,12925],{"class":3136,"line":4385},[3138,12924,3861],{"class":3832},[3138,12926,3837],{"class":3836},[3138,12928,12929,12931,12934],{"class":3136,"line":4393},[3138,12930,3868],{"class":3836},[3138,12932,12933],{"class":3687},"\"5432:5432\"",[3138,12935,12936],{"class":3697},"  # Проброс для доступу з хоста\n",[3114,12938,12939],{},[3120,12940,12941],{},"Production override (docker-compose.prod.yml):",[3670,12943,12945],{"className":3823,"code":12944,"language":3825,"meta":3675,"style":3675},"services:\n  backend:\n    restart: always\n    environment:\n      - NODE_ENV=production\n    deploy:\n      resources:\n        limits:\n          cpus: '0.5'\n          memory: 512M\n\n  db:\n    restart: always\n    volumes:\n      - db-data:/var/lib/postgresql/data\n\nvolumes:\n  db-data:\n",[3335,12946,12947,12953,12959,12967,12973,12979,12986,12993,13000,13010,13020,13024,13030,13038,13044,13050,13054,13060],{"__ignoreMap":3675},[3138,12948,12949,12951],{"class":3136,"line":3680},[3138,12950,3833],{"class":3832},[3138,12952,3837],{"class":3836},[3138,12954,12955,12957],{"class":3136,"line":3694},[3138,12956,4590],{"class":3832},[3138,12958,3837],{"class":3836},[3138,12960,12961,12963,12965],{"class":3136,"line":3755},[3138,12962,6986],{"class":3832},[3138,12964,3852],{"class":3836},[3138,12966,11289],{"class":3855},[3138,12968,12969,12971],{"class":3136,"line":3768},[3138,12970,4325],{"class":3832},[3138,12972,3837],{"class":3836},[3138,12974,12975,12977],{"class":3136,"line":3775},[3138,12976,3868],{"class":3836},[3138,12978,9549],{"class":3855},[3138,12980,12981,12984],{"class":3136,"line":3781},[3138,12982,12983],{"class":3832},"    deploy",[3138,12985,3837],{"class":3836},[3138,12987,12988,12991],{"class":3136,"line":4165},[3138,12989,12990],{"class":3832},"      resources",[3138,12992,3837],{"class":3836},[3138,12994,12995,12998],{"class":3136,"line":4178},[3138,12996,12997],{"class":3832},"        limits",[3138,12999,3837],{"class":3836},[3138,13001,13002,13005,13007],{"class":3136,"line":4189},[3138,13003,13004],{"class":3832},"          cpus",[3138,13006,3852],{"class":3836},[3138,13008,13009],{"class":3855},"'0.5'\n",[3138,13011,13012,13015,13017],{"class":3136,"line":4377},[3138,13013,13014],{"class":3832},"          memory",[3138,13016,3852],{"class":3836},[3138,13018,13019],{"class":3855},"512M\n",[3138,13021,13022],{"class":3136,"line":4385},[3138,13023,3772],{"emptyLinePlaceholder":3771},[3138,13025,13026,13028],{"class":3136,"line":4393},[3138,13027,4309],{"class":3832},[3138,13029,3837],{"class":3836},[3138,13031,13032,13034,13036],{"class":3136,"line":4401},[3138,13033,6986],{"class":3832},[3138,13035,3852],{"class":3836},[3138,13037,11289],{"class":3855},[3138,13039,13040,13042],{"class":3136,"line":4406},[3138,13041,4372],{"class":3832},[3138,13043,3837],{"class":3836},[3138,13045,13046,13048],{"class":3136,"line":4414},[3138,13047,3868],{"class":3836},[3138,13049,9842],{"class":3855},[3138,13051,13052],{"class":3136,"line":4424},[3138,13053,3772],{"emptyLinePlaceholder":3771},[3138,13055,13056,13058],{"class":3136,"line":4431},[3138,13057,4217],{"class":3832},[3138,13059,3837],{"class":3836},[3138,13061,13062,13064],{"class":3136,"line":4438},[3138,13063,4554],{"class":3832},[3138,13065,3837],{"class":3836},[3114,13067,13068],{},[3120,13069,13070],{},"Використання:",[3670,13072,13074],{"className":3672,"code":13073,"language":3674,"meta":3675,"style":3675},"# Development (автоматично використовує docker-compose.override.yml)\ndocker compose up -d\n\n# Production (явно вказати файл)\ndocker compose -f docker-compose.yml -f docker-compose.prod.yml up -d\n\n# Кілька override-файлів (застосовуються послідовно)\ndocker compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.local.yml up -d\n",[3335,13075,13076,13081,13091,13095,13100,13118,13122,13127],{"__ignoreMap":3675},[3138,13077,13078],{"class":3136,"line":3680},[3138,13079,13080],{"class":3697},"# Development (автоматично використовує docker-compose.override.yml)\n",[3138,13082,13083,13085,13087,13089],{"class":3136,"line":3694},[3138,13084,3684],{"class":3683},[3138,13086,3688],{"class":3687},[3138,13088,4919],{"class":3687},[3138,13090,4922],{"class":3725},[3138,13092,13093],{"class":3136,"line":3755},[3138,13094,3772],{"emptyLinePlaceholder":3771},[3138,13096,13097],{"class":3136,"line":3768},[3138,13098,13099],{"class":3697},"# Production (явно вказати файл)\n",[3138,13101,13102,13104,13106,13108,13110,13112,13114,13116],{"class":3136,"line":3775},[3138,13103,3684],{"class":3683},[3138,13105,3688],{"class":3687},[3138,13107,8872],{"class":3725},[3138,13109,8972],{"class":3687},[3138,13111,8872],{"class":3725},[3138,13113,8977],{"class":3687},[3138,13115,4919],{"class":3687},[3138,13117,4922],{"class":3725},[3138,13119,13120],{"class":3136,"line":3781},[3138,13121,3772],{"emptyLinePlaceholder":3771},[3138,13123,13124],{"class":3136,"line":4165},[3138,13125,13126],{"class":3697},"# Кілька override-файлів (застосовуються послідовно)\n",[3138,13128,13129,13131,13133,13135,13137,13139,13142,13144,13147,13149],{"class":3136,"line":4178},[3138,13130,3684],{"class":3683},[3138,13132,3688],{"class":3687},[3138,13134,8872],{"class":3725},[3138,13136,8972],{"class":3687},[3138,13138,8872],{"class":3725},[3138,13140,13141],{"class":3687}," docker-compose.override.yml",[3138,13143,8872],{"class":3725},[3138,13145,13146],{"class":3687}," docker-compose.local.yml",[3138,13148,4919],{"class":3687},[3138,13150,4922],{"class":3725},[3114,13152,13153],{},[3120,13154,13155],{},"Пріоритет:",[3442,13157,13158,13163,13169],{},[3328,13159,13160,13162],{},[3335,13161,3409],{}," (базова конфігурація)",[3328,13164,13165,13168],{},[3335,13166,13167],{},"docker-compose.override.yml"," (якщо існує, застосовується автоматично)",[3328,13170,13171,13172,13175],{},"Додаткові файли через ",[3335,13173,13174],{},"-f"," (застосовуються у порядку вказання)",[3641,13177,13178,13183],{},[3114,13179,13180],{},[3120,13181,13182],{},"Naming convention:",[3325,13184,13185,13190,13195,13200],{},[3328,13186,13187,13189],{},[3335,13188,3409],{}," — базова конфігурація (спільна для всіх середовищ)",[3328,13191,13192,13194],{},[3335,13193,13167],{}," — development (не комітити, якщо містить локальні налаштування)",[3328,13196,13197,13199],{},[3335,13198,9011],{}," — production",[3328,13201,13202,13205],{},[3335,13203,13204],{},"docker-compose.test.yml"," — testing/CI",[3423,13207,13209],{"id":13208},"secrets-управління-чутливими-даними","Secrets: управління чутливими даними",[3114,13211,13212,13214,13215,13217,13218,13220],{},[3120,13213,12267],{}," Паролі та API-ключі у ",[3335,13216,3409],{}," або ",[3335,13219,7274],{}," — небезпечно для production.",[3114,13222,13223,13225],{},[3120,13224,3377],{}," Docker Secrets (для Docker Swarm) або зовнішні secret managers.",[3114,13227,13228],{},[3120,13229,13230],{},"Docker Secrets (Swarm mode):",[3670,13232,13234],{"className":3823,"code":13233,"language":3825,"meta":3675,"style":3675},"services:\n  db:\n    image: postgres:16\n    secrets:\n      - db_password\n    environment:\n      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password\n\nsecrets:\n  db_password:\n    file: ./secrets/db_password.txt\n",[3335,13235,13236,13242,13248,13256,13263,13270,13276,13283,13287,13294,13301],{"__ignoreMap":3675},[3138,13237,13238,13240],{"class":3136,"line":3680},[3138,13239,3833],{"class":3832},[3138,13241,3837],{"class":3836},[3138,13243,13244,13246],{"class":3136,"line":3694},[3138,13245,4309],{"class":3832},[3138,13247,3837],{"class":3836},[3138,13249,13250,13252,13254],{"class":3136,"line":3755},[3138,13251,3849],{"class":3832},[3138,13253,3852],{"class":3836},[3138,13255,9829],{"class":3855},[3138,13257,13258,13261],{"class":3136,"line":3768},[3138,13259,13260],{"class":3832},"    secrets",[3138,13262,3837],{"class":3836},[3138,13264,13265,13267],{"class":3136,"line":3775},[3138,13266,3868],{"class":3836},[3138,13268,13269],{"class":3855},"db_password\n",[3138,13271,13272,13274],{"class":3136,"line":3781},[3138,13273,4325],{"class":3832},[3138,13275,3837],{"class":3836},[3138,13277,13278,13280],{"class":3136,"line":4165},[3138,13279,3868],{"class":3836},[3138,13281,13282],{"class":3855},"POSTGRES_PASSWORD_FILE=/run/secrets/db_password\n",[3138,13284,13285],{"class":3136,"line":4178},[3138,13286,3772],{"emptyLinePlaceholder":3771},[3138,13288,13289,13292],{"class":3136,"line":4189},[3138,13290,13291],{"class":3832},"secrets",[3138,13293,3837],{"class":3836},[3138,13295,13296,13299],{"class":3136,"line":4377},[3138,13297,13298],{"class":3832},"  db_password",[3138,13300,3837],{"class":3836},[3138,13302,13303,13306,13308],{"class":3136,"line":4385},[3138,13304,13305],{"class":3832},"    file",[3138,13307,3852],{"class":3836},[3138,13309,13310],{"class":3855},"./secrets/db_password.txt\n",[3114,13312,13313],{},[3120,13314,13315,13316,3820],{},"Файл ",[3335,13317,13318],{},"./secrets/db_password.txt",[3670,13320,13323],{"className":13321,"code":13322,"language":5082},[5080],"my_super_secret_password\n",[3335,13324,13322],{"__ignoreMap":3675},[3114,13326,13327,13328,13331],{},"Docker монтує секрет як файл у ",[3335,13329,13330],{},"/run/secrets/db_password"," всередині контейнера. PostgreSQL читає пароль з файлу.",[3114,13333,13334],{},[3120,13335,13336],{},"Для non-Swarm (альтернатива):",[3114,13338,13339],{},"Використовуйте зовнішні secret managers (HashiCorp Vault, AWS Secrets Manager) або змінні середовища з CI/CD.",[3670,13341,13343],{"className":3823,"code":13342,"language":3825,"meta":3675,"style":3675},"services:\n  backend:\n    image: myapp-api\n    environment:\n      - DATABASE_PASSWORD=${DATABASE_PASSWORD}  # З CI/CD\n",[3335,13344,13345,13351,13357,13365,13371],{"__ignoreMap":3675},[3138,13346,13347,13349],{"class":3136,"line":3680},[3138,13348,3833],{"class":3832},[3138,13350,3837],{"class":3836},[3138,13352,13353,13355],{"class":3136,"line":3694},[3138,13354,4590],{"class":3832},[3138,13356,3837],{"class":3836},[3138,13358,13359,13361,13363],{"class":3136,"line":3755},[3138,13360,3849],{"class":3832},[3138,13362,3852],{"class":3836},[3138,13364,10232],{"class":3855},[3138,13366,13367,13369],{"class":3136,"line":3768},[3138,13368,4325],{"class":3832},[3138,13370,3837],{"class":3836},[3138,13372,13373,13375,13378],{"class":3136,"line":3775},[3138,13374,3868],{"class":3836},[3138,13376,13377],{"class":3855},"DATABASE_PASSWORD=${DATABASE_PASSWORD}",[3138,13379,13380],{"class":3697},"  # З CI/CD\n",[3114,13382,13383],{},"У CI/CD (GitHub Actions, GitLab CI):",[3670,13385,13387],{"className":3823,"code":13386,"language":3825,"meta":3675,"style":3675},"# .github/workflows/deploy.yml\nenv:\n  DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}\n",[3335,13388,13389,13394,13400],{"__ignoreMap":3675},[3138,13390,13391],{"class":3136,"line":3680},[3138,13392,13393],{"class":3697},"# .github/workflows/deploy.yml\n",[3138,13395,13396,13398],{"class":3136,"line":3694},[3138,13397,5712],{"class":3832},[3138,13399,3837],{"class":3836},[3138,13401,13402,13405,13407],{"class":3136,"line":3755},[3138,13403,13404],{"class":3832},"  DATABASE_PASSWORD",[3138,13406,3852],{"class":3836},[3138,13408,13409],{"class":3855},"${{ secrets.DATABASE_PASSWORD }}\n",[5924,13411,13412,9740,13415,4687,13418,9746,13420,13423],{},[3120,13413,13414],{},"Не комітьте секрети:",[3335,13416,13417],{},"secrets/",[3335,13419,9745],{},[3335,13421,13422],{},"secrets.example/"," з placeholder-файлами.",[3416,13425],{},[3109,13427,13429],{"id":13428},"реальний-приклад-full-stack-застосунок","Реальний приклад: Full-stack застосунок",[3114,13431,13432],{},"Цей приклад демонструє створення повноцінного production-ready застосунку. Замість того, щоб сприймати його як один великий файл, давайте розглянемо його архітектуру пошарово:",[3442,13434,13435,13441,13447],{},[3328,13436,13437,13440],{},[3120,13438,13439],{},"Шар роутингу (Nginx):"," Приймає трафік з інтернету, віддає статику (Frontend) і проксіює API-запити на Backend.",[3328,13442,13443,13446],{},[3120,13444,13445],{},"Шар бізнес-логіки (Backend & Frontend):"," .NET API та React застосунок.",[3328,13448,13449,13452],{},[3120,13450,13451],{},"Шар даних (DB & Cache):"," PostgreSQL для стійкого зберігання та Redis для тимчасового кешування.",[3114,13454,13455],{},[3398,13456],{"alt":13457,"className":13458,"src":13459},"Повна архітектура full-stack застосунку в Docker Compose: nginx, frontend, backend, PostgreSQL, Redis, дві мережі та persistent volumes",[3402],"/images/tools/docker/docker-compose-basics/06.jpeg",[3670,13461,13463],{"className":7629,"code":13462,"language":7625,"meta":3675,"style":3675},"graph TD\n    Client([Клієнт / Browser]) -->|HTTP :80| Nginx[Nginx Reverse Proxy]\n    \n    subgraph frontend-net [Frontend Network]\n        Nginx -->|/| React[React Frontend]\n        Nginx -->|/api| API[.NET Backend]\n    end\n    \n    subgraph backend-net [Backend Network]\n        API -->|TCP 5432| DB[(PostgreSQL)]\n        API -->|TCP 6379| Cache[(Redis)]\n    end\n    \n    classDef proxy fill:#f9f,stroke:#333,stroke-width:2px;\n    class Nginx proxy;\n",[3335,13464,13465,13470,13475,13480,13485,13490,13495,13500,13504,13509,13514,13519,13523,13527,13532],{"__ignoreMap":3675},[3138,13466,13467],{"class":3136,"line":3680},[3138,13468,13469],{},"graph TD\n",[3138,13471,13472],{"class":3136,"line":3694},[3138,13473,13474],{},"    Client([Клієнт / Browser]) -->|HTTP :80| Nginx[Nginx Reverse Proxy]\n",[3138,13476,13477],{"class":3136,"line":3755},[3138,13478,13479],{},"    \n",[3138,13481,13482],{"class":3136,"line":3768},[3138,13483,13484],{},"    subgraph frontend-net [Frontend Network]\n",[3138,13486,13487],{"class":3136,"line":3775},[3138,13488,13489],{},"        Nginx -->|/| React[React Frontend]\n",[3138,13491,13492],{"class":3136,"line":3781},[3138,13493,13494],{},"        Nginx -->|/api| API[.NET Backend]\n",[3138,13496,13497],{"class":3136,"line":4165},[3138,13498,13499],{},"    end\n",[3138,13501,13502],{"class":3136,"line":4178},[3138,13503,13479],{},[3138,13505,13506],{"class":3136,"line":4189},[3138,13507,13508],{},"    subgraph backend-net [Backend Network]\n",[3138,13510,13511],{"class":3136,"line":4377},[3138,13512,13513],{},"        API -->|TCP 5432| DB[(PostgreSQL)]\n",[3138,13515,13516],{"class":3136,"line":4385},[3138,13517,13518],{},"        API -->|TCP 6379| Cache[(Redis)]\n",[3138,13520,13521],{"class":3136,"line":4393},[3138,13522,13499],{},[3138,13524,13525],{"class":3136,"line":4401},[3138,13526,13479],{},[3138,13528,13529],{"class":3136,"line":4406},[3138,13530,13531],{},"    classDef proxy fill:#f9f,stroke:#333,stroke-width:2px;\n",[3138,13533,13534],{"class":3136,"line":4414},[3138,13535,13536],{},"    class Nginx proxy;\n",[3423,13538,13540],{"id":13539},"структура-проєкту","Структура проєкту",[6186,13542,13543],{},[3325,13544,13545],{},[3328,13546,13547,13548],{},"myapp/\n",[3325,13549,13550,13552,13554,13556,13558,13571,13583],{},[3328,13551,3409],{},[3328,13553,13167],{},[3328,13555,9011],{},[3328,13557,9749],{},[3328,13559,13560,13561],{},"frontend/\n",[3325,13562,13563,13565,13568],{},[3328,13564,9324],{},[3328,13566,13567],{},"package.json",[3328,13569,13570],{},"src/",[3328,13572,13573,13574],{},"backend/\n",[3325,13575,13576,13578,13581],{},[3328,13577,9324],{},[3328,13579,13580],{},"MyApp.csproj",[3328,13582,7585],{},[3328,13584,13585,13586],{},"nginx/\n",[3325,13587,13588],{},[3328,13589,13590],{},"nginx.conf",[3423,13592,13594],{"id":13593},"docker-composeyml-базова-конфігурація","docker-compose.yml (базова конфігурація)",[3670,13596,13598],{"className":3823,"code":13597,"language":3825,"meta":3675,"style":3675},"services:\n  # PostgreSQL Database\n  db:\n    image: postgres:16-alpine\n    environment:\n      POSTGRES_DB: ${POSTGRES_DB:-myapp}\n      POSTGRES_USER: ${POSTGRES_USER:-postgres}\n      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme}\n    volumes:\n      - db-data:/var/lib/postgresql/data\n    networks:\n      - backend-net\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n      interval: 10s\n      timeout: 5s\n      retries: 5\n\n  # Redis Cache\n  cache:\n    image: redis:7-alpine\n    command: redis-server --appendonly yes\n    volumes:\n      - redis-data:/data\n    networks:\n      - backend-net\n    healthcheck:\n      test: [\"CMD\", \"redis-cli\", \"ping\"]\n      interval: 10s\n      timeout: 5s\n      retries: 5\n\n  # .NET Backend API\n  backend:\n    build:\n      context: ./backend\n      dockerfile: Dockerfile\n    image: myapp-backend:latest\n    environment:\n      - ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT:-Production}\n      - ConnectionStrings__DefaultConnection=Host=db;Database=${POSTGRES_DB:-myapp};Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-changeme}\n      - Redis__ConnectionString=cache:6379\n    networks:\n      - frontend-net\n      - backend-net\n    depends_on:\n      db:\n        condition: service_healthy\n      cache:\n        condition: service_healthy\n\n  # React Frontend\n  frontend:\n    build:\n      context: ./frontend\n      dockerfile: Dockerfile\n      args:\n        - REACT_APP_API_URL=${REACT_APP_API_URL:-http://localhost/api}\n    image: myapp-frontend:latest\n    networks:\n      - frontend-net\n    depends_on:\n      - backend\n\n  # Nginx Reverse Proxy\n  nginx:\n    image: nginx:alpine\n    ports:\n      - \"${NGINX_PORT:-80}:80\"\n    volumes:\n      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro\n    networks:\n      - frontend-net\n    depends_on:\n      - frontend\n      - backend\n\nnetworks:\n  frontend-net:\n    driver: bridge\n  backend-net:\n    driver: bridge\n\nvolumes:\n  db-data:\n  redis-data:\n",[3335,13599,13600,13606,13611,13617,13625,13631,13639,13647,13656,13662,13668,13674,13680,13686,13700,13708,13716,13724,13728,13733,13739,13747,13756,13762,13768,13774,13780,13786,13806,13814,13822,13830,13834,13839,13845,13851,13859,13867,13875,13881,13888,13895,13902,13908,13914,13920,13926,13932,13940,13946,13954,13958,13963,13969,13975,13984,13992,13998,14005,14014,14020,14026,14032,14038,14042,14047,14053,14062,14069,14077,14084,14092,14099,14106,14113,14120,14127,14132,14139,14146,14155,14162,14171,14176,14183,14190],{"__ignoreMap":3675},[3138,13601,13602,13604],{"class":3136,"line":3680},[3138,13603,3833],{"class":3832},[3138,13605,3837],{"class":3836},[3138,13607,13608],{"class":3136,"line":3694},[3138,13609,13610],{"class":3697},"  # PostgreSQL Database\n",[3138,13612,13613,13615],{"class":3136,"line":3755},[3138,13614,4309],{"class":3832},[3138,13616,3837],{"class":3836},[3138,13618,13619,13621,13623],{"class":3136,"line":3768},[3138,13620,3849],{"class":3832},[3138,13622,3852],{"class":3836},[3138,13624,7010],{"class":3855},[3138,13626,13627,13629],{"class":3136,"line":3775},[3138,13628,4325],{"class":3832},[3138,13630,3837],{"class":3836},[3138,13632,13633,13635,13637],{"class":3136,"line":3781},[3138,13634,7021],{"class":3832},[3138,13636,3852],{"class":3836},[3138,13638,7026],{"class":3855},[3138,13640,13641,13643,13645],{"class":3136,"line":4165},[3138,13642,7031],{"class":3832},[3138,13644,3852],{"class":3836},[3138,13646,7036],{"class":3855},[3138,13648,13649,13651,13653],{"class":3136,"line":4178},[3138,13650,7041],{"class":3832},[3138,13652,3852],{"class":3836},[3138,13654,13655],{"class":3855},"${POSTGRES_PASSWORD:-changeme}\n",[3138,13657,13658,13660],{"class":3136,"line":4189},[3138,13659,4372],{"class":3832},[3138,13661,3837],{"class":3836},[3138,13663,13664,13666],{"class":3136,"line":4377},[3138,13665,3868],{"class":3836},[3138,13667,9842],{"class":3855},[3138,13669,13670,13672],{"class":3136,"line":4385},[3138,13671,4388],{"class":3832},[3138,13673,3837],{"class":3836},[3138,13675,13676,13678],{"class":3136,"line":4393},[3138,13677,3868],{"class":3836},[3138,13679,10251],{"class":3855},[3138,13681,13682,13684],{"class":3136,"line":4401},[3138,13683,5328],{"class":3832},[3138,13685,3837],{"class":3836},[3138,13687,13688,13690,13692,13694,13696,13698],{"class":3136,"line":4406},[3138,13689,5335],{"class":3832},[3138,13691,5338],{"class":3836},[3138,13693,7086],{"class":3687},[3138,13695,4272],{"class":3836},[3138,13697,11105],{"class":3687},[3138,13699,5359],{"class":3836},[3138,13701,13702,13704,13706],{"class":3136,"line":4414},[3138,13703,5364],{"class":3832},[3138,13705,3852],{"class":3836},[3138,13707,5369],{"class":3855},[3138,13709,13710,13712,13714],{"class":3136,"line":4424},[3138,13711,5374],{"class":3832},[3138,13713,3852],{"class":3836},[3138,13715,5379],{"class":3855},[3138,13717,13718,13720,13722],{"class":3136,"line":4431},[3138,13719,5384],{"class":3832},[3138,13721,3852],{"class":3836},[3138,13723,5390],{"class":5389},[3138,13725,13726],{"class":3136,"line":4438},[3138,13727,3772],{"emptyLinePlaceholder":3771},[3138,13729,13730],{"class":3136,"line":4445},[3138,13731,13732],{"class":3697},"  # Redis Cache\n",[3138,13734,13735,13737],{"class":3136,"line":4456},[3138,13736,8007],{"class":3832},[3138,13738,3837],{"class":3836},[3138,13740,13741,13743,13745],{"class":3136,"line":4466},[3138,13742,3849],{"class":3832},[3138,13744,3852],{"class":3836},[3138,13746,8018],{"class":3855},[3138,13748,13749,13751,13753],{"class":3136,"line":4476},[3138,13750,5199],{"class":3832},[3138,13752,3852],{"class":3836},[3138,13754,13755],{"class":3855},"redis-server --appendonly yes\n",[3138,13757,13758,13760],{"class":3136,"line":4486},[3138,13759,4372],{"class":3832},[3138,13761,3837],{"class":3836},[3138,13763,13764,13766],{"class":3136,"line":4493},[3138,13765,3868],{"class":3836},[3138,13767,8040],{"class":3855},[3138,13769,13770,13772],{"class":3136,"line":4501},[3138,13771,4388],{"class":3832},[3138,13773,3837],{"class":3836},[3138,13775,13776,13778],{"class":3136,"line":4508},[3138,13777,3868],{"class":3836},[3138,13779,10251],{"class":3855},[3138,13781,13782,13784],{"class":3136,"line":4515},[3138,13783,5328],{"class":3832},[3138,13785,3837],{"class":3836},[3138,13787,13788,13790,13792,13794,13796,13799,13801,13804],{"class":3136,"line":4523},[3138,13789,5335],{"class":3832},[3138,13791,5338],{"class":3836},[3138,13793,5341],{"class":3687},[3138,13795,4272],{"class":3836},[3138,13797,13798],{"class":3687},"\"redis-cli\"",[3138,13800,4272],{"class":3836},[3138,13802,13803],{"class":3687},"\"ping\"",[3138,13805,5359],{"class":3836},[3138,13807,13808,13810,13812],{"class":3136,"line":4531},[3138,13809,5364],{"class":3832},[3138,13811,3852],{"class":3836},[3138,13813,5369],{"class":3855},[3138,13815,13816,13818,13820],{"class":3136,"line":4539},[3138,13817,5374],{"class":3832},[3138,13819,3852],{"class":3836},[3138,13821,5379],{"class":3855},[3138,13823,13824,13826,13828],{"class":3136,"line":4544},[3138,13825,5384],{"class":3832},[3138,13827,3852],{"class":3836},[3138,13829,5390],{"class":5389},[3138,13831,13832],{"class":3136,"line":4551},[3138,13833,3772],{"emptyLinePlaceholder":3771},[3138,13835,13836],{"class":3136,"line":4559},[3138,13837,13838],{"class":3697},"  # .NET Backend API\n",[3138,13840,13841,13843],{"class":3136,"line":4567},[3138,13842,4590],{"class":3832},[3138,13844,3837],{"class":3836},[3138,13846,13847,13849],{"class":3136,"line":4572},[3138,13848,6874],{"class":3832},[3138,13850,3837],{"class":3836},[3138,13852,13853,13855,13857],{"class":3136,"line":4579},[3138,13854,6881],{"class":3832},[3138,13856,3852],{"class":3836},[3138,13858,9209],{"class":3855},[3138,13860,13861,13863,13865],{"class":3136,"line":4587},[3138,13862,6891],{"class":3832},[3138,13864,3852],{"class":3836},[3138,13866,6896],{"class":3855},[3138,13868,13869,13871,13873],{"class":3136,"line":5442},[3138,13870,3849],{"class":3832},[3138,13872,3852],{"class":3836},[3138,13874,9473],{"class":3855},[3138,13876,13877,13879],{"class":3136,"line":5453},[3138,13878,4325],{"class":3832},[3138,13880,3837],{"class":3836},[3138,13882,13883,13885],{"class":3136,"line":5463},[3138,13884,3868],{"class":3836},[3138,13886,13887],{"class":3855},"ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT:-Production}\n",[3138,13889,13890,13892],{"class":3136,"line":5470},[3138,13891,3868],{"class":3836},[3138,13893,13894],{"class":3855},"ConnectionStrings__DefaultConnection=Host=db;Database=${POSTGRES_DB:-myapp};Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-changeme}\n",[3138,13896,13897,13899],{"class":3136,"line":5477},[3138,13898,3868],{"class":3836},[3138,13900,13901],{"class":3855},"Redis__ConnectionString=cache:6379\n",[3138,13903,13904,13906],{"class":3136,"line":5484},[3138,13905,4388],{"class":3832},[3138,13907,3837],{"class":3836},[3138,13909,13910,13912],{"class":3136,"line":5491},[3138,13911,3868],{"class":3836},[3138,13913,10213],{"class":3855},[3138,13915,13916,13918],{"class":3136,"line":5500},[3138,13917,3868],{"class":3836},[3138,13919,10251],{"class":3855},[3138,13921,13922,13924],{"class":3136,"line":5508},[3138,13923,4526],{"class":3832},[3138,13925,3837],{"class":3836},[3138,13927,13928,13930],{"class":3136,"line":5516},[3138,13929,6971],{"class":3832},[3138,13931,3837],{"class":3836},[3138,13933,13934,13936,13938],{"class":3136,"line":5521},[3138,13935,5273],{"class":3832},[3138,13937,3852],{"class":3836},[3138,13939,5278],{"class":3855},[3138,13941,13942,13944],{"class":3136,"line":5528},[3138,13943,7870],{"class":3832},[3138,13945,3837],{"class":3836},[3138,13947,13948,13950,13952],{"class":3136,"line":5536},[3138,13949,5273],{"class":3832},[3138,13951,3852],{"class":3836},[3138,13953,5278],{"class":3855},[3138,13955,13956],{"class":3136,"line":5541},[3138,13957,3772],{"emptyLinePlaceholder":3771},[3138,13959,13960],{"class":3136,"line":5548},[3138,13961,13962],{"class":3697},"  # React Frontend\n",[3138,13964,13965,13967],{"class":3136,"line":7237},[3138,13966,4582],{"class":3832},[3138,13968,3837],{"class":3836},[3138,13970,13971,13973],{"class":3136,"line":7245},[3138,13972,6874],{"class":3832},[3138,13974,3837],{"class":3836},[3138,13976,13977,13979,13981],{"class":3136,"line":7253},[3138,13978,6881],{"class":3832},[3138,13980,3852],{"class":3836},[3138,13982,13983],{"class":3855},"./frontend\n",[3138,13985,13986,13988,13990],{"class":3136,"line":7258},[3138,13987,6891],{"class":3832},[3138,13989,3852],{"class":3836},[3138,13991,6896],{"class":3855},[3138,13993,13994,13996],{"class":3136,"line":7265},[3138,13995,9222],{"class":3832},[3138,13997,3837],{"class":3836},[3138,13999,14000,14002],{"class":3136,"line":8113},[3138,14001,9229],{"class":3836},[3138,14003,14004],{"class":3855},"REACT_APP_API_URL=${REACT_APP_API_URL:-http://localhost/api}\n",[3138,14006,14007,14009,14011],{"class":3136,"line":8120},[3138,14008,3849],{"class":3832},[3138,14010,3852],{"class":3836},[3138,14012,14013],{"class":3855},"myapp-frontend:latest\n",[3138,14015,14016,14018],{"class":3136,"line":8127},[3138,14017,4388],{"class":3832},[3138,14019,3837],{"class":3836},[3138,14021,14022,14024],{"class":3136,"line":8132},[3138,14023,3868],{"class":3836},[3138,14025,10213],{"class":3855},[3138,14027,14028,14030],{"class":3136,"line":8139},[3138,14029,4526],{"class":3832},[3138,14031,3837],{"class":3836},[3138,14033,14034,14036],{"class":3136,"line":8146},[3138,14035,3868],{"class":3836},[3138,14037,4398],{"class":3855},[3138,14039,14040],{"class":3136,"line":8154},[3138,14041,3772],{"emptyLinePlaceholder":3771},[3138,14043,14044],{"class":3136,"line":8159},[3138,14045,14046],{"class":3697},"  # Nginx Reverse Proxy\n",[3138,14048,14049,14051],{"class":3136,"line":8166},[3138,14050,12224],{"class":3832},[3138,14052,3837],{"class":3836},[3138,14054,14056,14058,14060],{"class":3136,"line":14055},67,[3138,14057,3849],{"class":3832},[3138,14059,3852],{"class":3836},[3138,14061,3856],{"class":3855},[3138,14063,14065,14067],{"class":3136,"line":14064},68,[3138,14066,3861],{"class":3832},[3138,14068,3837],{"class":3836},[3138,14070,14072,14074],{"class":3136,"line":14071},69,[3138,14073,3868],{"class":3836},[3138,14075,14076],{"class":3687},"\"${NGINX_PORT:-80}:80\"\n",[3138,14078,14080,14082],{"class":3136,"line":14079},70,[3138,14081,4372],{"class":3832},[3138,14083,3837],{"class":3836},[3138,14085,14087,14089],{"class":3136,"line":14086},71,[3138,14088,3868],{"class":3836},[3138,14090,14091],{"class":3855},"./nginx/nginx.conf:/etc/nginx/nginx.conf:ro\n",[3138,14093,14095,14097],{"class":3136,"line":14094},72,[3138,14096,4388],{"class":3832},[3138,14098,3837],{"class":3836},[3138,14100,14102,14104],{"class":3136,"line":14101},73,[3138,14103,3868],{"class":3836},[3138,14105,10213],{"class":3855},[3138,14107,14109,14111],{"class":3136,"line":14108},74,[3138,14110,4526],{"class":3832},[3138,14112,3837],{"class":3836},[3138,14114,14116,14118],{"class":3136,"line":14115},75,[3138,14117,3868],{"class":3836},[3138,14119,4520],{"class":3855},[3138,14121,14123,14125],{"class":3136,"line":14122},76,[3138,14124,3868],{"class":3836},[3138,14126,4398],{"class":3855},[3138,14128,14130],{"class":3136,"line":14129},77,[3138,14131,3772],{"emptyLinePlaceholder":3771},[3138,14133,14135,14137],{"class":3136,"line":14134},78,[3138,14136,4213],{"class":3832},[3138,14138,3837],{"class":3836},[3138,14140,14142,14144],{"class":3136,"line":14141},79,[3138,14143,10296],{"class":3832},[3138,14145,3837],{"class":3836},[3138,14147,14149,14151,14153],{"class":3136,"line":14148},80,[3138,14150,10303],{"class":3832},[3138,14152,3852],{"class":3836},[3138,14154,10308],{"class":3855},[3138,14156,14158,14160],{"class":3136,"line":14157},81,[3138,14159,10313],{"class":3832},[3138,14161,3837],{"class":3836},[3138,14163,14165,14167,14169],{"class":3136,"line":14164},82,[3138,14166,10303],{"class":3832},[3138,14168,3852],{"class":3836},[3138,14170,10308],{"class":3855},[3138,14172,14174],{"class":3136,"line":14173},83,[3138,14175,3772],{"emptyLinePlaceholder":3771},[3138,14177,14179,14181],{"class":3136,"line":14178},84,[3138,14180,4217],{"class":3832},[3138,14182,3837],{"class":3836},[3138,14184,14186,14188],{"class":3136,"line":14185},85,[3138,14187,4554],{"class":3832},[3138,14189,3837],{"class":3836},[3138,14191,14193,14195],{"class":3136,"line":14192},86,[3138,14194,8149],{"class":3832},[3138,14196,3837],{"class":3836},[3423,14198,14200],{"id":14199},"docker-composeoverrideyml-development","docker-compose.override.yml (development)",[3670,14202,14204],{"className":3823,"code":14203,"language":3825,"meta":3675,"style":3675},"services:\n  db:\n    ports:\n      - \"5432:5432\"  # Доступ до БД з хоста\n\n  cache:\n    ports:\n      - \"6379:6379\"  # Доступ до Redis з хоста\n\n  backend:\n    build:\n      context: ./backend\n      target: development  # Multi-stage Dockerfile\n    volumes:\n      - ./backend:/app  # Hot-reload\n    environment:\n      - ASPNETCORE_ENVIRONMENT=Development\n    ports:\n      - \"5000:5000\"  # Прямий доступ до API\n\n  frontend:\n    build:\n      context: ./frontend\n      target: development\n    volumes:\n      - ./frontend/src:/app/src  # Hot-reload\n      - /app/node_modules\n    environment:\n      - REACT_APP_API_URL=http://localhost:5000\n    ports:\n      - \"3000:3000\"  # Прямий доступ до React dev server\n\n  # Adminer для перегляду БД\n  adminer:\n    image: adminer\n    ports:\n      - \"8080:8080\"\n    networks:\n      - backend-net\n    profiles:\n      - debug\n",[3335,14205,14206,14212,14218,14224,14233,14237,14243,14249,14259,14263,14269,14275,14283,14294,14300,14309,14315,14322,14328,14338,14342,14348,14354,14362,14370,14376,14385,14391,14397,14403,14409,14419,14423,14428,14434,14442,14448,14454,14460,14466,14472],{"__ignoreMap":3675},[3138,14207,14208,14210],{"class":3136,"line":3680},[3138,14209,3833],{"class":3832},[3138,14211,3837],{"class":3836},[3138,14213,14214,14216],{"class":3136,"line":3694},[3138,14215,4309],{"class":3832},[3138,14217,3837],{"class":3836},[3138,14219,14220,14222],{"class":3136,"line":3755},[3138,14221,3861],{"class":3832},[3138,14223,3837],{"class":3836},[3138,14225,14226,14228,14230],{"class":3136,"line":3768},[3138,14227,3868],{"class":3836},[3138,14229,12933],{"class":3687},[3138,14231,14232],{"class":3697},"  # Доступ до БД з хоста\n",[3138,14234,14235],{"class":3136,"line":3775},[3138,14236,3772],{"emptyLinePlaceholder":3771},[3138,14238,14239,14241],{"class":3136,"line":3781},[3138,14240,8007],{"class":3832},[3138,14242,3837],{"class":3836},[3138,14244,14245,14247],{"class":3136,"line":4165},[3138,14246,3861],{"class":3832},[3138,14248,3837],{"class":3836},[3138,14250,14251,14253,14256],{"class":3136,"line":4178},[3138,14252,3868],{"class":3836},[3138,14254,14255],{"class":3687},"\"6379:6379\"",[3138,14257,14258],{"class":3697},"  # Доступ до Redis з хоста\n",[3138,14260,14261],{"class":3136,"line":4189},[3138,14262,3772],{"emptyLinePlaceholder":3771},[3138,14264,14265,14267],{"class":3136,"line":4377},[3138,14266,4590],{"class":3832},[3138,14268,3837],{"class":3836},[3138,14270,14271,14273],{"class":3136,"line":4385},[3138,14272,6874],{"class":3832},[3138,14274,3837],{"class":3836},[3138,14276,14277,14279,14281],{"class":3136,"line":4393},[3138,14278,6881],{"class":3832},[3138,14280,3852],{"class":3836},[3138,14282,9209],{"class":3855},[3138,14284,14285,14287,14289,14291],{"class":3136,"line":4401},[3138,14286,7743],{"class":3832},[3138,14288,3852],{"class":3836},[3138,14290,5605],{"class":3855},[3138,14292,14293],{"class":3697},"  # Multi-stage Dockerfile\n",[3138,14295,14296,14298],{"class":3136,"line":4406},[3138,14297,4372],{"class":3832},[3138,14299,3837],{"class":3836},[3138,14301,14302,14304,14307],{"class":3136,"line":4414},[3138,14303,3868],{"class":3836},[3138,14305,14306],{"class":3855},"./backend:/app",[3138,14308,12890],{"class":3697},[3138,14310,14311,14313],{"class":3136,"line":4424},[3138,14312,4325],{"class":3832},[3138,14314,3837],{"class":3836},[3138,14316,14317,14319],{"class":3136,"line":4431},[3138,14318,3868],{"class":3836},[3138,14320,14321],{"class":3855},"ASPNETCORE_ENVIRONMENT=Development\n",[3138,14323,14324,14326],{"class":3136,"line":4438},[3138,14325,3861],{"class":3832},[3138,14327,3837],{"class":3836},[3138,14329,14330,14332,14335],{"class":3136,"line":4445},[3138,14331,3868],{"class":3836},[3138,14333,14334],{"class":3687},"\"5000:5000\"",[3138,14336,14337],{"class":3697},"  # Прямий доступ до API\n",[3138,14339,14340],{"class":3136,"line":4456},[3138,14341,3772],{"emptyLinePlaceholder":3771},[3138,14343,14344,14346],{"class":3136,"line":4466},[3138,14345,4582],{"class":3832},[3138,14347,3837],{"class":3836},[3138,14349,14350,14352],{"class":3136,"line":4476},[3138,14351,6874],{"class":3832},[3138,14353,3837],{"class":3836},[3138,14355,14356,14358,14360],{"class":3136,"line":4486},[3138,14357,6881],{"class":3832},[3138,14359,3852],{"class":3836},[3138,14361,13983],{"class":3855},[3138,14363,14364,14366,14368],{"class":3136,"line":4493},[3138,14365,7743],{"class":3832},[3138,14367,3852],{"class":3836},[3138,14369,5243],{"class":3855},[3138,14371,14372,14374],{"class":3136,"line":4501},[3138,14373,4372],{"class":3832},[3138,14375,3837],{"class":3836},[3138,14377,14378,14380,14383],{"class":3136,"line":4508},[3138,14379,3868],{"class":3836},[3138,14381,14382],{"class":3855},"./frontend/src:/app/src",[3138,14384,12890],{"class":3697},[3138,14386,14387,14389],{"class":3136,"line":4515},[3138,14388,3868],{"class":3836},[3138,14390,7779],{"class":3855},[3138,14392,14393,14395],{"class":3136,"line":4523},[3138,14394,4325],{"class":3832},[3138,14396,3837],{"class":3836},[3138,14398,14399,14401],{"class":3136,"line":4531},[3138,14400,3868],{"class":3836},[3138,14402,9305],{"class":3855},[3138,14404,14405,14407],{"class":3136,"line":4539},[3138,14406,3861],{"class":3832},[3138,14408,3837],{"class":3836},[3138,14410,14411,14413,14416],{"class":3136,"line":4544},[3138,14412,3868],{"class":3836},[3138,14414,14415],{"class":3687},"\"3000:3000\"",[3138,14417,14418],{"class":3697},"  # Прямий доступ до React dev server\n",[3138,14420,14421],{"class":3136,"line":4551},[3138,14422,3772],{"emptyLinePlaceholder":3771},[3138,14424,14425],{"class":3136,"line":4559},[3138,14426,14427],{"class":3697},"  # Adminer для перегляду БД\n",[3138,14429,14430,14432],{"class":3136,"line":4567},[3138,14431,12559],{"class":3832},[3138,14433,3837],{"class":3836},[3138,14435,14436,14438,14440],{"class":3136,"line":4572},[3138,14437,3849],{"class":3832},[3138,14439,3852],{"class":3836},[3138,14441,12570],{"class":3855},[3138,14443,14444,14446],{"class":3136,"line":4579},[3138,14445,3861],{"class":3832},[3138,14447,3837],{"class":3836},[3138,14449,14450,14452],{"class":3136,"line":4587},[3138,14451,3868],{"class":3836},[3138,14453,12583],{"class":3687},[3138,14455,14456,14458],{"class":3136,"line":5442},[3138,14457,4388],{"class":3832},[3138,14459,3837],{"class":3836},[3138,14461,14462,14464],{"class":3136,"line":5453},[3138,14463,3868],{"class":3836},[3138,14465,10251],{"class":3855},[3138,14467,14468,14470],{"class":3136,"line":5463},[3138,14469,5503],{"class":3832},[3138,14471,3837],{"class":3836},[3138,14473,14474,14476],{"class":3136,"line":5470},[3138,14475,3868],{"class":3836},[3138,14477,5513],{"class":3855},[3423,14479,14481],{"id":14480},"docker-composeprodyml-production","docker-compose.prod.yml (production)",[3670,14483,14485],{"className":3823,"code":14484,"language":3825,"meta":3675,"style":3675},"services:\n  db:\n    restart: always\n    deploy:\n      resources:\n        limits:\n          cpus: '1'\n          memory: 1G\n\n  cache:\n    restart: always\n    deploy:\n      resources:\n        limits:\n          cpus: '0.5'\n          memory: 512M\n\n  backend:\n    restart: always\n    environment:\n      - ASPNETCORE_ENVIRONMENT=Production\n    deploy:\n      replicas: 2  # Масштабування\n      resources:\n        limits:\n          cpus: '1'\n          memory: 1G\n\n  frontend:\n    restart: always\n\n  nginx:\n    restart: always\n    ports:\n      - \"80:80\"\n      - \"443:443\"\n    volumes:\n      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro\n      - ./ssl:/etc/nginx/ssl:ro  # SSL сертифікати\n",[3335,14486,14487,14493,14499,14507,14513,14519,14525,14534,14543,14547,14553,14561,14567,14573,14579,14587,14595,14599,14605,14613,14619,14626,14632,14645,14651,14657,14665,14673,14677,14683,14691,14695,14701,14709,14715,14721,14728,14734,14740],{"__ignoreMap":3675},[3138,14488,14489,14491],{"class":3136,"line":3680},[3138,14490,3833],{"class":3832},[3138,14492,3837],{"class":3836},[3138,14494,14495,14497],{"class":3136,"line":3694},[3138,14496,4309],{"class":3832},[3138,14498,3837],{"class":3836},[3138,14500,14501,14503,14505],{"class":3136,"line":3755},[3138,14502,6986],{"class":3832},[3138,14504,3852],{"class":3836},[3138,14506,11289],{"class":3855},[3138,14508,14509,14511],{"class":3136,"line":3768},[3138,14510,12983],{"class":3832},[3138,14512,3837],{"class":3836},[3138,14514,14515,14517],{"class":3136,"line":3775},[3138,14516,12990],{"class":3832},[3138,14518,3837],{"class":3836},[3138,14520,14521,14523],{"class":3136,"line":3781},[3138,14522,12997],{"class":3832},[3138,14524,3837],{"class":3836},[3138,14526,14527,14529,14531],{"class":3136,"line":4165},[3138,14528,13004],{"class":3832},[3138,14530,3852],{"class":3836},[3138,14532,14533],{"class":3855},"'1'\n",[3138,14535,14536,14538,14540],{"class":3136,"line":4178},[3138,14537,13014],{"class":3832},[3138,14539,3852],{"class":3836},[3138,14541,14542],{"class":3855},"1G\n",[3138,14544,14545],{"class":3136,"line":4189},[3138,14546,3772],{"emptyLinePlaceholder":3771},[3138,14548,14549,14551],{"class":3136,"line":4377},[3138,14550,8007],{"class":3832},[3138,14552,3837],{"class":3836},[3138,14554,14555,14557,14559],{"class":3136,"line":4385},[3138,14556,6986],{"class":3832},[3138,14558,3852],{"class":3836},[3138,14560,11289],{"class":3855},[3138,14562,14563,14565],{"class":3136,"line":4393},[3138,14564,12983],{"class":3832},[3138,14566,3837],{"class":3836},[3138,14568,14569,14571],{"class":3136,"line":4401},[3138,14570,12990],{"class":3832},[3138,14572,3837],{"class":3836},[3138,14574,14575,14577],{"class":3136,"line":4406},[3138,14576,12997],{"class":3832},[3138,14578,3837],{"class":3836},[3138,14580,14581,14583,14585],{"class":3136,"line":4414},[3138,14582,13004],{"class":3832},[3138,14584,3852],{"class":3836},[3138,14586,13009],{"class":3855},[3138,14588,14589,14591,14593],{"class":3136,"line":4424},[3138,14590,13014],{"class":3832},[3138,14592,3852],{"class":3836},[3138,14594,13019],{"class":3855},[3138,14596,14597],{"class":3136,"line":4431},[3138,14598,3772],{"emptyLinePlaceholder":3771},[3138,14600,14601,14603],{"class":3136,"line":4438},[3138,14602,4590],{"class":3832},[3138,14604,3837],{"class":3836},[3138,14606,14607,14609,14611],{"class":3136,"line":4445},[3138,14608,6986],{"class":3832},[3138,14610,3852],{"class":3836},[3138,14612,11289],{"class":3855},[3138,14614,14615,14617],{"class":3136,"line":4456},[3138,14616,4325],{"class":3832},[3138,14618,3837],{"class":3836},[3138,14620,14621,14623],{"class":3136,"line":4466},[3138,14622,3868],{"class":3836},[3138,14624,14625],{"class":3855},"ASPNETCORE_ENVIRONMENT=Production\n",[3138,14627,14628,14630],{"class":3136,"line":4476},[3138,14629,12983],{"class":3832},[3138,14631,3837],{"class":3836},[3138,14633,14634,14637,14639,14642],{"class":3136,"line":4486},[3138,14635,14636],{"class":3832},"      replicas",[3138,14638,3852],{"class":3836},[3138,14640,14641],{"class":5389},"2",[3138,14643,14644],{"class":3697},"  # Масштабування\n",[3138,14646,14647,14649],{"class":3136,"line":4493},[3138,14648,12990],{"class":3832},[3138,14650,3837],{"class":3836},[3138,14652,14653,14655],{"class":3136,"line":4501},[3138,14654,12997],{"class":3832},[3138,14656,3837],{"class":3836},[3138,14658,14659,14661,14663],{"class":3136,"line":4508},[3138,14660,13004],{"class":3832},[3138,14662,3852],{"class":3836},[3138,14664,14533],{"class":3855},[3138,14666,14667,14669,14671],{"class":3136,"line":4515},[3138,14668,13014],{"class":3832},[3138,14670,3852],{"class":3836},[3138,14672,14542],{"class":3855},[3138,14674,14675],{"class":3136,"line":4523},[3138,14676,3772],{"emptyLinePlaceholder":3771},[3138,14678,14679,14681],{"class":3136,"line":4531},[3138,14680,4582],{"class":3832},[3138,14682,3837],{"class":3836},[3138,14684,14685,14687,14689],{"class":3136,"line":4539},[3138,14686,6986],{"class":3832},[3138,14688,3852],{"class":3836},[3138,14690,11289],{"class":3855},[3138,14692,14693],{"class":3136,"line":4544},[3138,14694,3772],{"emptyLinePlaceholder":3771},[3138,14696,14697,14699],{"class":3136,"line":4551},[3138,14698,12224],{"class":3832},[3138,14700,3837],{"class":3836},[3138,14702,14703,14705,14707],{"class":3136,"line":4559},[3138,14704,6986],{"class":3832},[3138,14706,3852],{"class":3836},[3138,14708,11289],{"class":3855},[3138,14710,14711,14713],{"class":3136,"line":4567},[3138,14712,3861],{"class":3832},[3138,14714,3837],{"class":3836},[3138,14716,14717,14719],{"class":3136,"line":4572},[3138,14718,3868],{"class":3836},[3138,14720,12247],{"class":3687},[3138,14722,14723,14725],{"class":3136,"line":4579},[3138,14724,3868],{"class":3836},[3138,14726,14727],{"class":3687},"\"443:443\"\n",[3138,14729,14730,14732],{"class":3136,"line":4587},[3138,14731,4372],{"class":3832},[3138,14733,3837],{"class":3836},[3138,14735,14736,14738],{"class":3136,"line":5442},[3138,14737,3868],{"class":3836},[3138,14739,14091],{"class":3855},[3138,14741,14742,14744,14747],{"class":3136,"line":5453},[3138,14743,3868],{"class":3836},[3138,14745,14746],{"class":3855},"./ssl:/etc/nginx/ssl:ro",[3138,14748,14749],{"class":3697},"  # SSL сертифікати\n",[3423,14751,9749],{"id":14752},"envexample",[3670,14754,14756],{"className":7272,"code":14755,"language":5712,"meta":3675,"style":3675},"# Database\nPOSTGRES_DB=myapp\nPOSTGRES_USER=postgres\nPOSTGRES_PASSWORD=changeme\n\n# Backend\nASPNETCORE_ENVIRONMENT=Development\n\n# Frontend\nREACT_APP_API_URL=http://localhost/api\n\n# Nginx\nNGINX_PORT=80\n",[3335,14757,14758,14763,14767,14771,14775,14779,14784,14788,14792,14797,14802,14806,14811],{"__ignoreMap":3675},[3138,14759,14760],{"class":3136,"line":3680},[3138,14761,14762],{},"# Database\n",[3138,14764,14765],{"class":3136,"line":3694},[3138,14766,7281],{},[3138,14768,14769],{"class":3136,"line":3755},[3138,14770,7286],{},[3138,14772,14773],{"class":3136,"line":3768},[3138,14774,12841],{},[3138,14776,14777],{"class":3136,"line":3775},[3138,14778,3772],{"emptyLinePlaceholder":3771},[3138,14780,14781],{"class":3136,"line":3781},[3138,14782,14783],{},"# Backend\n",[3138,14785,14786],{"class":3136,"line":4165},[3138,14787,14321],{},[3138,14789,14790],{"class":3136,"line":4178},[3138,14791,3772],{"emptyLinePlaceholder":3771},[3138,14793,14794],{"class":3136,"line":4189},[3138,14795,14796],{},"# Frontend\n",[3138,14798,14799],{"class":3136,"line":4377},[3138,14800,14801],{},"REACT_APP_API_URL=http://localhost/api\n",[3138,14803,14804],{"class":3136,"line":4385},[3138,14805,3772],{"emptyLinePlaceholder":3771},[3138,14807,14808],{"class":3136,"line":4393},[3138,14809,14810],{},"# Nginx\n",[3138,14812,14813],{"class":3136,"line":4401},[3138,14814,14815],{},"NGINX_PORT=80\n",[3423,14817,14819],{"id":14818},"nginxnginxconf","nginx/nginx.conf",[3670,14821,14825],{"className":14822,"code":14823,"language":14824,"meta":3675,"style":3675},"language-nginx shiki shiki-themes light-plus dark-plus dark-plus","events {\n    worker_connections 1024;\n}\n\nhttp {\n    upstream backend {\n        server backend:5000;\n    }\n\n    upstream frontend {\n        server frontend:3000;\n    }\n\n    server {\n        listen 80;\n        server_name localhost;\n\n        # Frontend\n        location / {\n            proxy_pass http://frontend;\n            proxy_set_header Host $host;\n            proxy_set_header X-Real-IP $remote_addr;\n        }\n\n        # Backend API\n        location /api/ {\n            proxy_pass http://backend/;\n            proxy_set_header Host $host;\n            proxy_set_header X-Real-IP $remote_addr;\n        }\n\n        # Health check\n        location /health {\n            access_log off;\n            return 200 \"OK\\n\";\n            add_header Content-Type text/plain;\n        }\n    }\n}\n","nginx",[3335,14826,14827,14832,14837,14841,14845,14850,14855,14860,14864,14868,14873,14878,14882,14886,14891,14896,14901,14905,14910,14915,14920,14925,14930,14935,14939,14944,14949,14954,14958,14962,14966,14970,14975,14980,14985,14990,14995,14999,15003],{"__ignoreMap":3675},[3138,14828,14829],{"class":3136,"line":3680},[3138,14830,14831],{},"events {\n",[3138,14833,14834],{"class":3136,"line":3694},[3138,14835,14836],{},"    worker_connections 1024;\n",[3138,14838,14839],{"class":3136,"line":3755},[3138,14840,6023],{},[3138,14842,14843],{"class":3136,"line":3768},[3138,14844,3772],{"emptyLinePlaceholder":3771},[3138,14846,14847],{"class":3136,"line":3775},[3138,14848,14849],{},"http {\n",[3138,14851,14852],{"class":3136,"line":3781},[3138,14853,14854],{},"    upstream backend {\n",[3138,14856,14857],{"class":3136,"line":4165},[3138,14858,14859],{},"        server backend:5000;\n",[3138,14861,14862],{"class":3136,"line":4178},[3138,14863,11012],{},[3138,14865,14866],{"class":3136,"line":4189},[3138,14867,3772],{"emptyLinePlaceholder":3771},[3138,14869,14870],{"class":3136,"line":4377},[3138,14871,14872],{},"    upstream frontend {\n",[3138,14874,14875],{"class":3136,"line":4385},[3138,14876,14877],{},"        server frontend:3000;\n",[3138,14879,14880],{"class":3136,"line":4393},[3138,14881,11012],{},[3138,14883,14884],{"class":3136,"line":4401},[3138,14885,3772],{"emptyLinePlaceholder":3771},[3138,14887,14888],{"class":3136,"line":4406},[3138,14889,14890],{},"    server {\n",[3138,14892,14893],{"class":3136,"line":4414},[3138,14894,14895],{},"        listen 80;\n",[3138,14897,14898],{"class":3136,"line":4424},[3138,14899,14900],{},"        server_name localhost;\n",[3138,14902,14903],{"class":3136,"line":4431},[3138,14904,3772],{"emptyLinePlaceholder":3771},[3138,14906,14907],{"class":3136,"line":4438},[3138,14908,14909],{},"        # Frontend\n",[3138,14911,14912],{"class":3136,"line":4445},[3138,14913,14914],{},"        location / {\n",[3138,14916,14917],{"class":3136,"line":4456},[3138,14918,14919],{},"            proxy_pass http://frontend;\n",[3138,14921,14922],{"class":3136,"line":4466},[3138,14923,14924],{},"            proxy_set_header Host $host;\n",[3138,14926,14927],{"class":3136,"line":4476},[3138,14928,14929],{},"            proxy_set_header X-Real-IP $remote_addr;\n",[3138,14931,14932],{"class":3136,"line":4486},[3138,14933,14934],{},"        }\n",[3138,14936,14937],{"class":3136,"line":4493},[3138,14938,3772],{"emptyLinePlaceholder":3771},[3138,14940,14941],{"class":3136,"line":4501},[3138,14942,14943],{},"        # Backend API\n",[3138,14945,14946],{"class":3136,"line":4508},[3138,14947,14948],{},"        location /api/ {\n",[3138,14950,14951],{"class":3136,"line":4515},[3138,14952,14953],{},"            proxy_pass http://backend/;\n",[3138,14955,14956],{"class":3136,"line":4523},[3138,14957,14924],{},[3138,14959,14960],{"class":3136,"line":4531},[3138,14961,14929],{},[3138,14963,14964],{"class":3136,"line":4539},[3138,14965,14934],{},[3138,14967,14968],{"class":3136,"line":4544},[3138,14969,3772],{"emptyLinePlaceholder":3771},[3138,14971,14972],{"class":3136,"line":4551},[3138,14973,14974],{},"        # Health check\n",[3138,14976,14977],{"class":3136,"line":4559},[3138,14978,14979],{},"        location /health {\n",[3138,14981,14982],{"class":3136,"line":4567},[3138,14983,14984],{},"            access_log off;\n",[3138,14986,14987],{"class":3136,"line":4572},[3138,14988,14989],{},"            return 200 \"OK\\n\";\n",[3138,14991,14992],{"class":3136,"line":4579},[3138,14993,14994],{},"            add_header Content-Type text/plain;\n",[3138,14996,14997],{"class":3136,"line":4587},[3138,14998,14934],{},[3138,15000,15001],{"class":3136,"line":5442},[3138,15002,11012],{},[3138,15004,15005],{"class":3136,"line":5453},[3138,15006,6023],{},[3423,15008,15010],{"id":15009},"використання","Використання",[3114,15012,15013],{},[3120,15014,15015],{},"Development:",[3670,15017,15019],{"className":3672,"code":15018,"language":3674,"meta":3675,"style":3675},"# Створити .env з .env.example\ncp .env.example .env\n\n# Запустити (автоматично використовує override)\ndocker compose up -d\n\n# З debug-інструментами\ndocker compose --profile debug up -d\n\n# Переглянути логи\ndocker compose logs -f backend\n\n# Зупинити\ndocker compose down\n",[3335,15020,15021,15026,15035,15039,15044,15054,15058,15063,15077,15081,15085,15097,15101,15106],{"__ignoreMap":3675},[3138,15022,15023],{"class":3136,"line":3680},[3138,15024,15025],{"class":3697},"# Створити .env з .env.example\n",[3138,15027,15028,15030,15032],{"class":3136,"line":3694},[3138,15029,7395],{"class":3683},[3138,15031,7398],{"class":3687},[3138,15033,15034],{"class":3687}," .env\n",[3138,15036,15037],{"class":3136,"line":3755},[3138,15038,3772],{"emptyLinePlaceholder":3771},[3138,15040,15041],{"class":3136,"line":3768},[3138,15042,15043],{"class":3697},"# Запустити (автоматично використовує override)\n",[3138,15045,15046,15048,15050,15052],{"class":3136,"line":3775},[3138,15047,3684],{"class":3683},[3138,15049,3688],{"class":3687},[3138,15051,4919],{"class":3687},[3138,15053,4922],{"class":3725},[3138,15055,15056],{"class":3136,"line":3781},[3138,15057,3772],{"emptyLinePlaceholder":3771},[3138,15059,15060],{"class":3136,"line":4165},[3138,15061,15062],{"class":3697},"# З debug-інструментами\n",[3138,15064,15065,15067,15069,15071,15073,15075],{"class":3136,"line":4178},[3138,15066,3684],{"class":3683},[3138,15068,3688],{"class":3687},[3138,15070,6107],{"class":3725},[3138,15072,6110],{"class":3687},[3138,15074,4919],{"class":3687},[3138,15076,4922],{"class":3725},[3138,15078,15079],{"class":3136,"line":4189},[3138,15080,3772],{"emptyLinePlaceholder":3771},[3138,15082,15083],{"class":3136,"line":4377},[3138,15084,8861],{"class":3697},[3138,15086,15087,15089,15091,15093,15095],{"class":3136,"line":4385},[3138,15088,3684],{"class":3683},[3138,15090,3688],{"class":3687},[3138,15092,5048],{"class":3687},[3138,15094,8872],{"class":3725},[3138,15096,9400],{"class":3687},[3138,15098,15099],{"class":3136,"line":4393},[3138,15100,3772],{"emptyLinePlaceholder":3771},[3138,15102,15103],{"class":3136,"line":4401},[3138,15104,15105],{"class":3697},"# Зупинити\n",[3138,15107,15108,15110,15112],{"class":3136,"line":4406},[3138,15109,3684],{"class":3683},[3138,15111,3688],{"class":3687},[3138,15113,4061],{"class":3687},[3114,15115,15116],{},[3120,15117,15118],{},"Production:",[3670,15120,15122],{"className":3672,"code":15121,"language":3674,"meta":3675,"style":3675},"# Встановити production-змінні у .env\nnano .env\n\n# Запустити з production-конфігурацією\ndocker compose -f docker-compose.yml -f docker-compose.prod.yml up -d\n\n# Перевірити статус\ndocker compose ps\n\n# Масштабувати backend\ndocker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --scale backend=3\n",[3335,15123,15124,15129,15136,15140,15145,15163,15167,15172,15180,15184,15189],{"__ignoreMap":3675},[3138,15125,15126],{"class":3136,"line":3680},[3138,15127,15128],{"class":3697},"# Встановити production-змінні у .env\n",[3138,15130,15131,15134],{"class":3136,"line":3694},[3138,15132,15133],{"class":3683},"nano",[3138,15135,15034],{"class":3687},[3138,15137,15138],{"class":3136,"line":3755},[3138,15139,3772],{"emptyLinePlaceholder":3771},[3138,15141,15142],{"class":3136,"line":3768},[3138,15143,15144],{"class":3697},"# Запустити з production-конфігурацією\n",[3138,15146,15147,15149,15151,15153,15155,15157,15159,15161],{"class":3136,"line":3775},[3138,15148,3684],{"class":3683},[3138,15150,3688],{"class":3687},[3138,15152,8872],{"class":3725},[3138,15154,8972],{"class":3687},[3138,15156,8872],{"class":3725},[3138,15158,8977],{"class":3687},[3138,15160,4919],{"class":3687},[3138,15162,4922],{"class":3725},[3138,15164,15165],{"class":3136,"line":3781},[3138,15166,3772],{"emptyLinePlaceholder":3771},[3138,15168,15169],{"class":3136,"line":4165},[3138,15170,15171],{"class":3697},"# Перевірити статус\n",[3138,15173,15174,15176,15178],{"class":3136,"line":4178},[3138,15175,3684],{"class":3683},[3138,15177,3688],{"class":3687},[3138,15179,4037],{"class":3687},[3138,15181,15182],{"class":3136,"line":4189},[3138,15183,3772],{"emptyLinePlaceholder":3771},[3138,15185,15186],{"class":3136,"line":4377},[3138,15187,15188],{"class":3697},"# Масштабувати backend\n",[3138,15190,15191,15193,15195,15197,15199,15201,15203,15205,15207,15209,15211],{"class":3136,"line":4385},[3138,15192,3684],{"class":3683},[3138,15194,3688],{"class":3687},[3138,15196,8872],{"class":3725},[3138,15198,8972],{"class":3687},[3138,15200,8872],{"class":3725},[3138,15202,8977],{"class":3687},[3138,15204,4919],{"class":3687},[3138,15206,6115],{"class":3725},[3138,15208,12122],{"class":3725},[3138,15210,12125],{"class":3687},[3138,15212,12128],{"class":5389},[3114,15214,15215],{},[3120,15216,15217],{},"CI/CD (GitHub Actions приклад):",[3670,15219,15221],{"className":3823,"code":15220,"language":3825,"meta":3675,"style":3675},"name: Deploy\n\non:\n  push:\n    branches: [main]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      \n      - name: Build images\n        run: docker compose -f docker-compose.yml -f docker-compose.prod.yml build\n      \n      - name: Push to registry\n        run: |\n          echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin\n          docker compose push\n      \n      - name: Deploy to server\n        run: |\n          ssh user@server \"cd /app && docker compose pull && docker compose up -d\"\n",[3335,15222,15223,15233,15237,15244,15251,15263,15267,15274,15281,15291,15298,15310,15315,15326,15336,15340,15351,15360,15365,15370,15374,15385,15393],{"__ignoreMap":3675},[3138,15224,15225,15228,15230],{"class":3136,"line":3680},[3138,15226,15227],{"class":3832},"name",[3138,15229,3852],{"class":3836},[3138,15231,15232],{"class":3855},"Deploy\n",[3138,15234,15235],{"class":3136,"line":3694},[3138,15236,3772],{"emptyLinePlaceholder":3771},[3138,15238,15239,15242],{"class":3136,"line":3755},[3138,15240,15241],{"class":3725},"on",[3138,15243,3837],{"class":3836},[3138,15245,15246,15249],{"class":3136,"line":3768},[3138,15247,15248],{"class":3832},"  push",[3138,15250,3837],{"class":3836},[3138,15252,15253,15256,15258,15261],{"class":3136,"line":3775},[3138,15254,15255],{"class":3832},"    branches",[3138,15257,5338],{"class":3836},[3138,15259,15260],{"class":3855},"main",[3138,15262,5359],{"class":3836},[3138,15264,15265],{"class":3136,"line":3781},[3138,15266,3772],{"emptyLinePlaceholder":3771},[3138,15268,15269,15272],{"class":3136,"line":4165},[3138,15270,15271],{"class":3832},"jobs",[3138,15273,3837],{"class":3836},[3138,15275,15276,15279],{"class":3136,"line":4178},[3138,15277,15278],{"class":3832},"  deploy",[3138,15280,3837],{"class":3836},[3138,15282,15283,15286,15288],{"class":3136,"line":4189},[3138,15284,15285],{"class":3832},"    runs-on",[3138,15287,3852],{"class":3836},[3138,15289,15290],{"class":3855},"ubuntu-latest\n",[3138,15292,15293,15296],{"class":3136,"line":4377},[3138,15294,15295],{"class":3832},"    steps",[3138,15297,3837],{"class":3836},[3138,15299,15300,15302,15305,15307],{"class":3136,"line":4385},[3138,15301,3868],{"class":3836},[3138,15303,15304],{"class":3832},"uses",[3138,15306,3852],{"class":3836},[3138,15308,15309],{"class":3855},"actions/checkout@v3\n",[3138,15311,15312],{"class":3136,"line":4393},[3138,15313,15314],{"class":3836},"      \n",[3138,15316,15317,15319,15321,15323],{"class":3136,"line":4401},[3138,15318,3868],{"class":3836},[3138,15320,15227],{"class":3832},[3138,15322,3852],{"class":3836},[3138,15324,15325],{"class":3855},"Build images\n",[3138,15327,15328,15331,15333],{"class":3136,"line":4406},[3138,15329,15330],{"class":3832},"        run",[3138,15332,3852],{"class":3836},[3138,15334,15335],{"class":3855},"docker compose -f docker-compose.yml -f docker-compose.prod.yml build\n",[3138,15337,15338],{"class":3136,"line":4414},[3138,15339,15314],{"class":3836},[3138,15341,15342,15344,15346,15348],{"class":3136,"line":4424},[3138,15343,3868],{"class":3836},[3138,15345,15227],{"class":3832},[3138,15347,3852],{"class":3836},[3138,15349,15350],{"class":3855},"Push to registry\n",[3138,15352,15353,15355,15357],{"class":3136,"line":4431},[3138,15354,15330],{"class":3832},[3138,15356,3852],{"class":3836},[3138,15358,15359],{"class":5625},"|\n",[3138,15361,15362],{"class":3136,"line":4438},[3138,15363,15364],{"class":3855},"          echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin\n",[3138,15366,15367],{"class":3136,"line":4445},[3138,15368,15369],{"class":3855},"          docker compose push\n",[3138,15371,15372],{"class":3136,"line":4456},[3138,15373,15314],{"class":3855},[3138,15375,15376,15378,15380,15382],{"class":3136,"line":4466},[3138,15377,3868],{"class":3836},[3138,15379,15227],{"class":3832},[3138,15381,3852],{"class":3836},[3138,15383,15384],{"class":3855},"Deploy to server\n",[3138,15386,15387,15389,15391],{"class":3136,"line":4476},[3138,15388,15330],{"class":3832},[3138,15390,3852],{"class":3836},[3138,15392,15359],{"class":5625},[3138,15394,15395],{"class":3136,"line":4486},[3138,15396,15397],{"class":3855},"          ssh user@server \"cd /app && docker compose pull && docker compose up -d\"\n",[3416,15399],{},[3109,15401,15403],{"id":15402},"troubleshooting-docker-compose","Troubleshooting Docker Compose",[3423,15405,15407],{"id":15406},"типові-проблеми","Типові проблеми",[15409,15410,15411,15520,15595,15672],"accordion",{},[15412,15413,15415,15420,15440,15445,15456,15460],"accordion-item",{"title":15414},"Сервіс не може підключитися до іншого сервісу",[3114,15416,15417],{},[3120,15418,15419],{},"Симптоми:",[3670,15421,15423],{"className":3672,"code":15422,"language":3674,"meta":3675,"style":3675},"docker compose logs backend\n# Помилка: Connection refused to db:5432\n",[3335,15424,15425,15435],{"__ignoreMap":3675},[3138,15426,15427,15429,15431,15433],{"class":3136,"line":3680},[3138,15428,3684],{"class":3683},[3138,15430,3688],{"class":3687},[3138,15432,5048],{"class":3687},[3138,15434,9400],{"class":3687},[3138,15436,15437],{"class":3136,"line":3694},[3138,15438,15439],{"class":3697},"# Помилка: Connection refused to db:5432\n",[3114,15441,15442],{},[3120,15443,15444],{},"Можливі причини:",[3442,15446,15447,15450,15453],{},[3328,15448,15449],{},"Сервіси в різних мережах",[3328,15451,15452],{},"База даних ще не готова (не пройшла health check)",[3328,15454,15455],{},"Неправильне ім'я хоста у connection string",[3114,15457,15458],{},[3120,15459,3377],{},[3670,15461,15463],{"className":3672,"code":15462,"language":3674,"meta":3675,"style":3675},"# Перевірити мережі\ndocker compose config | grep networks -A 10\n\n# Перевірити health check\ndocker compose ps\n\n# Додати health check та depends_on\n",[3335,15464,15465,15470,15494,15498,15503,15511,15515],{"__ignoreMap":3675},[3138,15466,15467],{"class":3136,"line":3680},[3138,15468,15469],{"class":3697},"# Перевірити мережі\n",[3138,15471,15472,15474,15476,15479,15482,15485,15488,15491],{"class":3136,"line":3694},[3138,15473,3684],{"class":3683},[3138,15475,3688],{"class":3687},[3138,15477,15478],{"class":3687}," config",[3138,15480,15481],{"class":3836}," | ",[3138,15483,15484],{"class":3683},"grep",[3138,15486,15487],{"class":3687}," networks",[3138,15489,15490],{"class":3725}," -A",[3138,15492,15493],{"class":5389}," 10\n",[3138,15495,15496],{"class":3136,"line":3755},[3138,15497,3772],{"emptyLinePlaceholder":3771},[3138,15499,15500],{"class":3136,"line":3768},[3138,15501,15502],{"class":3697},"# Перевірити health check\n",[3138,15504,15505,15507,15509],{"class":3136,"line":3775},[3138,15506,3684],{"class":3683},[3138,15508,3688],{"class":3687},[3138,15510,4037],{"class":3687},[3138,15512,15513],{"class":3136,"line":3781},[3138,15514,3772],{"emptyLinePlaceholder":3771},[3138,15516,15517],{"class":3136,"line":4165},[3138,15518,15519],{"class":3697},"# Додати health check та depends_on\n",[15412,15521,15523,15528,15532,15543,15547],{"title":15522},"Зміни у коді не відображаються",[3114,15524,15525,15527],{},[3120,15526,15419],{}," Змінили код, але контейнер використовує стару версію.",[3114,15529,15530],{},[3120,15531,15444],{},[3442,15533,15534,15537,15540],{},[3328,15535,15536],{},"Немає bind mount для hot-reload",[3328,15538,15539],{},"Образ не пересобрано після змін у Dockerfile",[3328,15541,15542],{},"Кеш Docker",[3114,15544,15545],{},[3120,15546,3377],{},[3670,15548,15550],{"className":3672,"code":15549,"language":3674,"meta":3675,"style":3675},"# Пересобрати образи\ndocker compose up --build\n\n# Пересобрати без кешу\ndocker compose build --no-cache\n\n# Додати bind mount для development\n",[3335,15551,15552,15557,15567,15571,15576,15586,15590],{"__ignoreMap":3675},[3138,15553,15554],{"class":3136,"line":3680},[3138,15555,15556],{"class":3697},"# Пересобрати образи\n",[3138,15558,15559,15561,15563,15565],{"class":3136,"line":3694},[3138,15560,3684],{"class":3683},[3138,15562,3688],{"class":3687},[3138,15564,4919],{"class":3687},[3138,15566,9362],{"class":3725},[3138,15568,15569],{"class":3136,"line":3755},[3138,15570,3772],{"emptyLinePlaceholder":3771},[3138,15572,15573],{"class":3136,"line":3768},[3138,15574,15575],{"class":3697},"# Пересобрати без кешу\n",[3138,15577,15578,15580,15582,15584],{"class":3136,"line":3775},[3138,15579,3684],{"class":3683},[3138,15581,3688],{"class":3687},[3138,15583,9397],{"class":3687},[3138,15585,11971],{"class":3725},[3138,15587,15588],{"class":3136,"line":3781},[3138,15589,3772],{"emptyLinePlaceholder":3771},[3138,15591,15592],{"class":3136,"line":4165},[3138,15593,15594],{"class":3697},"# Додати bind mount для development\n",[15412,15596,15598,15602,15620,15624],{"title":15597},"Port already in use",[3114,15599,15600],{},[3120,15601,15419],{},[3670,15603,15605],{"className":3672,"code":15604,"language":3674,"meta":3675,"style":3675},"docker compose up\n# Error: Bind for 0.0.0.0:8080 failed: port is already allocated\n",[3335,15606,15607,15615],{"__ignoreMap":3675},[3138,15608,15609,15611,15613],{"class":3136,"line":3680},[3138,15610,3684],{"class":3683},[3138,15612,3688],{"class":3687},[3138,15614,3890],{"class":3687},[3138,15616,15617],{"class":3136,"line":3694},[3138,15618,15619],{"class":3697},"# Error: Bind for 0.0.0.0:8080 failed: port is already allocated\n",[3114,15621,15622],{},[3120,15623,3377],{},[3670,15625,15627],{"className":3672,"code":15626,"language":3674,"meta":3675,"style":3675},"# Знайти процес, що використовує порт\nsudo lsof -i :8080\n\n# Або змінити порт у docker-compose.yml\nports:\n  - \"8081:80\"  # Використати інший порт хоста\n",[3335,15628,15629,15634,15647,15651,15656,15661],{"__ignoreMap":3675},[3138,15630,15631],{"class":3136,"line":3680},[3138,15632,15633],{"class":3697},"# Знайти процес, що використовує порт\n",[3138,15635,15636,15638,15641,15644],{"class":3136,"line":3694},[3138,15637,3719],{"class":3683},[3138,15639,15640],{"class":3687}," lsof",[3138,15642,15643],{"class":3725}," -i",[3138,15645,15646],{"class":3687}," :8080\n",[3138,15648,15649],{"class":3136,"line":3755},[3138,15650,3772],{"emptyLinePlaceholder":3771},[3138,15652,15653],{"class":3136,"line":3768},[3138,15654,15655],{"class":3697},"# Або змінити порт у docker-compose.yml\n",[3138,15657,15658],{"class":3136,"line":3775},[3138,15659,15660],{"class":3683},"ports:\n",[3138,15662,15663,15666,15669],{"class":3136,"line":3781},[3138,15664,15665],{"class":3683},"  -",[3138,15667,15668],{"class":3687}," \"8081:80\"",[3138,15670,15671],{"class":3697},"  # Використати інший порт хоста\n",[15412,15673,15675,15680,15684,15735],{"title":15674},"Volume permissions (Permission denied)",[3114,15676,15677,15679],{},[3120,15678,15419],{}," Контейнер не може писати у bind mount.",[3114,15681,15682],{},[3120,15683,3377],{},[3670,15685,15687],{"className":3823,"code":15686,"language":3825,"meta":3675,"style":3675},"services:\n  app:\n    image: myapp\n    user: \"${UID}:${GID}\"  # Використати UID/GID хоста\n    volumes:\n      - ./data:/app/data\n",[3335,15688,15689,15695,15701,15709,15722,15728],{"__ignoreMap":3675},[3138,15690,15691,15693],{"class":3136,"line":3680},[3138,15692,3833],{"class":3832},[3138,15694,3837],{"class":3836},[3138,15696,15697,15699],{"class":3136,"line":3694},[3138,15698,9937],{"class":3832},[3138,15700,3837],{"class":3836},[3138,15702,15703,15705,15707],{"class":3136,"line":3755},[3138,15704,3849],{"class":3832},[3138,15706,3852],{"class":3836},[3138,15708,9536],{"class":3855},[3138,15710,15711,15714,15716,15719],{"class":3136,"line":3768},[3138,15712,15713],{"class":3832},"    user",[3138,15715,3852],{"class":3836},[3138,15717,15718],{"class":3687},"\"${UID}:${GID}\"",[3138,15720,15721],{"class":3697},"  # Використати UID/GID хоста\n",[3138,15723,15724,15726],{"class":3136,"line":3775},[3138,15725,4372],{"class":3832},[3138,15727,3837],{"class":3836},[3138,15729,15730,15732],{"class":3136,"line":3781},[3138,15731,3868],{"class":3836},[3138,15733,15734],{"class":3855},"./data:/app/data\n",[3670,15736,15738],{"className":3672,"code":15737,"language":3674,"meta":3675,"style":3675},"# Встановити UID/GID у .env\necho \"UID=$(id -u)\" >> .env\necho \"GID=$(id -g)\" >> .env\n",[3335,15739,15740,15745,15766],{"__ignoreMap":3675},[3138,15741,15742],{"class":3136,"line":3680},[3138,15743,15744],{"class":3697},"# Встановити UID/GID у .env\n",[3138,15746,15747,15750,15753,15756,15758,15760,15763],{"class":3136,"line":3694},[3138,15748,15749],{"class":3683},"echo",[3138,15751,15752],{"class":3687}," \"UID=$(",[3138,15754,15755],{"class":3683},"id",[3138,15757,11859],{"class":3725},[3138,15759,3746],{"class":3687},[3138,15761,15762],{"class":3836}," >> ",[3138,15764,15765],{"class":3687},".env\n",[3138,15767,15768,15770,15773,15775,15778,15780,15782],{"class":3136,"line":3755},[3138,15769,15749],{"class":3683},[3138,15771,15772],{"class":3687}," \"GID=$(",[3138,15774,15755],{"class":3683},[3138,15776,15777],{"class":3725}," -g",[3138,15779,3746],{"class":3687},[3138,15781,15762],{"class":3836},[3138,15783,15765],{"class":3687},[3423,15785,15787],{"id":15786},"діагностичні-команди","Діагностичні команди",[3670,15789,15791],{"className":3672,"code":15790,"language":3674,"meta":3675,"style":3675},"# Перевірити конфігурацію (merged YAML)\ndocker compose config\n\n# Перевірити, які образи будуть використані\ndocker compose config --images\n\n# Перевірити, які volumes будуть створені\ndocker compose config --volumes\n\n# Валідація синтаксису\ndocker compose config --quiet\n\n# Переглянути події\ndocker compose events\n\n# Статистика ресурсів\ndocker stats $(docker compose ps -q)\n",[3335,15792,15793,15798,15807,15811,15816,15827,15831,15836,15847,15851,15856,15867,15871,15876,15885,15889,15894],{"__ignoreMap":3675},[3138,15794,15795],{"class":3136,"line":3680},[3138,15796,15797],{"class":3697},"# Перевірити конфігурацію (merged YAML)\n",[3138,15799,15800,15802,15804],{"class":3136,"line":3694},[3138,15801,3684],{"class":3683},[3138,15803,3688],{"class":3687},[3138,15805,15806],{"class":3687}," config\n",[3138,15808,15809],{"class":3136,"line":3755},[3138,15810,3772],{"emptyLinePlaceholder":3771},[3138,15812,15813],{"class":3136,"line":3768},[3138,15814,15815],{"class":3697},"# Перевірити, які образи будуть використані\n",[3138,15817,15818,15820,15822,15824],{"class":3136,"line":3775},[3138,15819,3684],{"class":3683},[3138,15821,3688],{"class":3687},[3138,15823,15478],{"class":3687},[3138,15825,15826],{"class":3725}," --images\n",[3138,15828,15829],{"class":3136,"line":3781},[3138,15830,3772],{"emptyLinePlaceholder":3771},[3138,15832,15833],{"class":3136,"line":4165},[3138,15834,15835],{"class":3697},"# Перевірити, які volumes будуть створені\n",[3138,15837,15838,15840,15842,15844],{"class":3136,"line":4178},[3138,15839,3684],{"class":3683},[3138,15841,3688],{"class":3687},[3138,15843,15478],{"class":3687},[3138,15845,15846],{"class":3725}," --volumes\n",[3138,15848,15849],{"class":3136,"line":4189},[3138,15850,3772],{"emptyLinePlaceholder":3771},[3138,15852,15853],{"class":3136,"line":4377},[3138,15854,15855],{"class":3697},"# Валідація синтаксису\n",[3138,15857,15858,15860,15862,15864],{"class":3136,"line":4385},[3138,15859,3684],{"class":3683},[3138,15861,3688],{"class":3687},[3138,15863,15478],{"class":3687},[3138,15865,15866],{"class":3725}," --quiet\n",[3138,15868,15869],{"class":3136,"line":4393},[3138,15870,3772],{"emptyLinePlaceholder":3771},[3138,15872,15873],{"class":3136,"line":4401},[3138,15874,15875],{"class":3697},"# Переглянути події\n",[3138,15877,15878,15880,15882],{"class":3136,"line":4406},[3138,15879,3684],{"class":3683},[3138,15881,3688],{"class":3687},[3138,15883,15884],{"class":3687}," events\n",[3138,15886,15887],{"class":3136,"line":4414},[3138,15888,3772],{"emptyLinePlaceholder":3771},[3138,15890,15891],{"class":3136,"line":4424},[3138,15892,15893],{"class":3697},"# Статистика ресурсів\n",[3138,15895,15896,15898,15901,15904,15906,15908,15911,15914],{"class":3136,"line":4431},[3138,15897,3684],{"class":3683},[3138,15899,15900],{"class":3687}," stats",[3138,15902,15903],{"class":3836}," $(",[3138,15905,3684],{"class":3683},[3138,15907,3688],{"class":3687},[3138,15909,15910],{"class":3687}," ps",[3138,15912,15913],{"class":3725}," -q",[3138,15915,5719],{"class":3836},[3416,15917],{},[3109,15919,15921],{"id":15920},"найкращі-практики-docker-compose","Найкращі практики Docker Compose",[3423,15923,15925],{"id":15924},"_1-використовуйте-env-для-конфігурації","1. Використовуйте .env для конфігурації",[3114,15927,15928],{},[3120,15929,15930],{},"Погано:",[3670,15932,15934],{"className":3823,"code":15933,"language":3825,"meta":3675,"style":3675},"services:\n  db:\n    environment:\n      - POSTGRES_PASSWORD=hardcoded_password  # Небезпечно\n",[3335,15935,15936,15942,15948,15954],{"__ignoreMap":3675},[3138,15937,15938,15940],{"class":3136,"line":3680},[3138,15939,3833],{"class":3832},[3138,15941,3837],{"class":3836},[3138,15943,15944,15946],{"class":3136,"line":3694},[3138,15945,4309],{"class":3832},[3138,15947,3837],{"class":3836},[3138,15949,15950,15952],{"class":3136,"line":3755},[3138,15951,4325],{"class":3832},[3138,15953,3837],{"class":3836},[3138,15955,15956,15958,15961],{"class":3136,"line":3768},[3138,15957,3868],{"class":3836},[3138,15959,15960],{"class":3855},"POSTGRES_PASSWORD=hardcoded_password",[3138,15962,15963],{"class":3697},"  # Небезпечно\n",[3114,15965,15966],{},[3120,15967,15968],{},"Добре:",[3670,15970,15972],{"className":3823,"code":15971,"language":3825,"meta":3675,"style":3675},"services:\n  db:\n    environment:\n      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}\n",[3335,15973,15974,15980,15986,15992],{"__ignoreMap":3675},[3138,15975,15976,15978],{"class":3136,"line":3680},[3138,15977,3833],{"class":3832},[3138,15979,3837],{"class":3836},[3138,15981,15982,15984],{"class":3136,"line":3694},[3138,15983,4309],{"class":3832},[3138,15985,3837],{"class":3836},[3138,15987,15988,15990],{"class":3136,"line":3755},[3138,15989,4325],{"class":3832},[3138,15991,3837],{"class":3836},[3138,15993,15994,15996],{"class":3136,"line":3768},[3138,15995,3868],{"class":3836},[3138,15997,15998],{"class":3855},"POSTGRES_PASSWORD=${POSTGRES_PASSWORD}\n",[3670,16000,16002],{"className":7272,"code":16001,"language":5712,"meta":3675,"style":3675},"# .env\nPOSTGRES_PASSWORD=secure_password_from_env\n",[3335,16003,16004,16009],{"__ignoreMap":3675},[3138,16005,16006],{"class":3136,"line":3680},[3138,16007,16008],{},"# .env\n",[3138,16010,16011],{"class":3136,"line":3694},[3138,16012,16013],{},"POSTGRES_PASSWORD=secure_password_from_env\n",[3423,16015,16017],{"id":16016},"_2-розділяйте-конфігурацію-за-середовищами","2. Розділяйте конфігурацію за середовищами",[3114,16019,16020],{},"Використовуйте override-файли для development/production замість одного великого файлу з умовами.",[3423,16022,16024],{"id":16023},"_3-використовуйте-health-checks","3. Використовуйте health checks",[3670,16026,16028],{"className":3823,"code":16027,"language":3825,"meta":3675,"style":3675},"services:\n  db:\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready\"]\n      interval: 10s\n      timeout: 5s\n      retries: 5\n",[3335,16029,16030,16036,16042,16048,16063,16071,16079],{"__ignoreMap":3675},[3138,16031,16032,16034],{"class":3136,"line":3680},[3138,16033,3833],{"class":3832},[3138,16035,3837],{"class":3836},[3138,16037,16038,16040],{"class":3136,"line":3694},[3138,16039,4309],{"class":3832},[3138,16041,3837],{"class":3836},[3138,16043,16044,16046],{"class":3136,"line":3755},[3138,16045,5328],{"class":3832},[3138,16047,3837],{"class":3836},[3138,16049,16050,16052,16054,16056,16058,16061],{"class":3136,"line":3768},[3138,16051,5335],{"class":3832},[3138,16053,5338],{"class":3836},[3138,16055,7086],{"class":3687},[3138,16057,4272],{"class":3836},[3138,16059,16060],{"class":3687},"\"pg_isready\"",[3138,16062,5359],{"class":3836},[3138,16064,16065,16067,16069],{"class":3136,"line":3775},[3138,16066,5364],{"class":3832},[3138,16068,3852],{"class":3836},[3138,16070,5369],{"class":3855},[3138,16072,16073,16075,16077],{"class":3136,"line":3781},[3138,16074,5374],{"class":3832},[3138,16076,3852],{"class":3836},[3138,16078,5379],{"class":3855},[3138,16080,16081,16083,16085],{"class":3136,"line":4165},[3138,16082,5384],{"class":3832},[3138,16084,3852],{"class":3836},[3138,16086,5390],{"class":5389},[3114,16088,16089,16090,16092],{},"Це дозволяє ",[3335,16091,4790],{}," чекати готовності сервісу.",[3423,16094,16096],{"id":16095},"_4-обмежуйте-ресурси-у-production","4. Обмежуйте ресурси у production",[3670,16098,16100],{"className":3823,"code":16099,"language":3825,"meta":3675,"style":3675},"services:\n  backend:\n    deploy:\n      resources:\n        limits:\n          cpus: '1'\n          memory: 1G\n        reservations:\n          cpus: '0.5'\n          memory: 512M\n",[3335,16101,16102,16108,16114,16120,16126,16132,16140,16148,16155,16163],{"__ignoreMap":3675},[3138,16103,16104,16106],{"class":3136,"line":3680},[3138,16105,3833],{"class":3832},[3138,16107,3837],{"class":3836},[3138,16109,16110,16112],{"class":3136,"line":3694},[3138,16111,4590],{"class":3832},[3138,16113,3837],{"class":3836},[3138,16115,16116,16118],{"class":3136,"line":3755},[3138,16117,12983],{"class":3832},[3138,16119,3837],{"class":3836},[3138,16121,16122,16124],{"class":3136,"line":3768},[3138,16123,12990],{"class":3832},[3138,16125,3837],{"class":3836},[3138,16127,16128,16130],{"class":3136,"line":3775},[3138,16129,12997],{"class":3832},[3138,16131,3837],{"class":3836},[3138,16133,16134,16136,16138],{"class":3136,"line":3781},[3138,16135,13004],{"class":3832},[3138,16137,3852],{"class":3836},[3138,16139,14533],{"class":3855},[3138,16141,16142,16144,16146],{"class":3136,"line":4165},[3138,16143,13014],{"class":3832},[3138,16145,3852],{"class":3836},[3138,16147,14542],{"class":3855},[3138,16149,16150,16153],{"class":3136,"line":4178},[3138,16151,16152],{"class":3832},"        reservations",[3138,16154,3837],{"class":3836},[3138,16156,16157,16159,16161],{"class":3136,"line":4189},[3138,16158,13004],{"class":3832},[3138,16160,3852],{"class":3836},[3138,16162,13009],{"class":3855},[3138,16164,16165,16167,16169],{"class":3136,"line":4377},[3138,16166,13014],{"class":3832},[3138,16168,3852],{"class":3836},[3138,16170,13019],{"class":3855},[3423,16172,16174],{"id":16173},"_5-використовуйте-named-volumes-для-даних","5. Використовуйте named volumes для даних",[3114,16176,16177],{},[3120,16178,15930],{},[3670,16180,16182],{"className":3823,"code":16181,"language":3825,"meta":3675,"style":3675},"volumes:\n  - /var/lib/postgresql/data  # Anonymous volume\n",[3335,16183,16184,16190],{"__ignoreMap":3675},[3138,16185,16186,16188],{"class":3136,"line":3680},[3138,16187,4217],{"class":3832},[3138,16189,3837],{"class":3836},[3138,16191,16192,16195,16198],{"class":3136,"line":3694},[3138,16193,16194],{"class":3836},"  - ",[3138,16196,16197],{"class":3855},"/var/lib/postgresql/data",[3138,16199,16200],{"class":3697},"  # Anonymous volume\n",[3114,16202,16203],{},[3120,16204,15968],{},[3670,16206,16208],{"className":3823,"code":16207,"language":3825,"meta":3675,"style":3675},"volumes:\n  - db-data:/var/lib/postgresql/data\n\nvolumes:\n  db-data:\n",[3335,16209,16210,16216,16222,16226,16232],{"__ignoreMap":3675},[3138,16211,16212,16214],{"class":3136,"line":3680},[3138,16213,4217],{"class":3832},[3138,16215,3837],{"class":3836},[3138,16217,16218,16220],{"class":3136,"line":3694},[3138,16219,16194],{"class":3836},[3138,16221,9842],{"class":3855},[3138,16223,16224],{"class":3136,"line":3755},[3138,16225,3772],{"emptyLinePlaceholder":3771},[3138,16227,16228,16230],{"class":3136,"line":3768},[3138,16229,4217],{"class":3832},[3138,16231,3837],{"class":3836},[3138,16233,16234,16236],{"class":3136,"line":3775},[3138,16235,4554],{"class":3832},[3138,16237,3837],{"class":3836},[3423,16239,16241],{"id":16240},"_6-документуйте-архітектуру","6. Документуйте архітектуру",[3114,16243,16244,16245,16247],{},"Додайте коментарі у ",[3335,16246,3409],{}," та створіть README з інструкціями.",[3670,16249,16251],{"className":3823,"code":16250,"language":3825,"meta":3675,"style":3675},"services:\n  # Backend API - .NET 8 Web API\n  # Доступний на http://localhost:5000 (development)\n  # Підключається до PostgreSQL та Redis\n  backend:\n    build: ./backend\n    # ...\n",[3335,16252,16253,16259,16264,16269,16274,16280,16288],{"__ignoreMap":3675},[3138,16254,16255,16257],{"class":3136,"line":3680},[3138,16256,3833],{"class":3832},[3138,16258,3837],{"class":3836},[3138,16260,16261],{"class":3136,"line":3694},[3138,16262,16263],{"class":3697},"  # Backend API - .NET 8 Web API\n",[3138,16265,16266],{"class":3136,"line":3755},[3138,16267,16268],{"class":3697},"  # Доступний на http://localhost:5000 (development)\n",[3138,16270,16271],{"class":3136,"line":3768},[3138,16272,16273],{"class":3697},"  # Підключається до PostgreSQL та Redis\n",[3138,16275,16276,16278],{"class":3136,"line":3775},[3138,16277,4590],{"class":3832},[3138,16279,3837],{"class":3836},[3138,16281,16282,16284,16286],{"class":3136,"line":3781},[3138,16283,6874],{"class":3832},[3138,16285,3852],{"class":3836},[3138,16287,9209],{"class":3855},[3138,16289,16290],{"class":3136,"line":4165},[3138,16291,16292],{"class":3697},"    # ...\n",[3423,16294,16296],{"id":16295},"_7-використовуйте-dockerignore","7. Використовуйте .dockerignore",[3114,16298,16299,16300,16303],{},"Створіть ",[3335,16301,16302],{},".dockerignore"," у кожній директорії з Dockerfile:",[3670,16305,16308],{"className":16306,"code":16307,"language":5082},[5080],"node_modules\n.git\n.env\n*.log\n",[3335,16309,16307],{"__ignoreMap":3675},[3114,16311,16312],{},"Це прискорює збірку та зменшує розмір build context.",[3423,16314,16316],{"id":16315},"_8-версіонуйте-образи","8. Версіонуйте образи",[3114,16318,16319],{},[3120,16320,15930],{},[3670,16322,16324],{"className":3823,"code":16323,"language":3825,"meta":3675,"style":3675},"services:\n  db:\n    image: postgres:latest  # Непередбачувано\n",[3335,16325,16326,16332,16338],{"__ignoreMap":3675},[3138,16327,16328,16330],{"class":3136,"line":3680},[3138,16329,3833],{"class":3832},[3138,16331,3837],{"class":3836},[3138,16333,16334,16336],{"class":3136,"line":3694},[3138,16335,4309],{"class":3832},[3138,16337,3837],{"class":3836},[3138,16339,16340,16342,16344,16347],{"class":3136,"line":3755},[3138,16341,3849],{"class":3832},[3138,16343,3852],{"class":3836},[3138,16345,16346],{"class":3855},"postgres:latest",[3138,16348,16349],{"class":3697},"  # Непередбачувано\n",[3114,16351,16352],{},[3120,16353,15968],{},[3670,16355,16357],{"className":3823,"code":16356,"language":3825,"meta":3675,"style":3675},"services:\n  db:\n    image: postgres:16.2-alpine  # Конкретна версія\n",[3335,16358,16359,16365,16371],{"__ignoreMap":3675},[3138,16360,16361,16363],{"class":3136,"line":3680},[3138,16362,3833],{"class":3832},[3138,16364,3837],{"class":3836},[3138,16366,16367,16369],{"class":3136,"line":3694},[3138,16368,4309],{"class":3832},[3138,16370,3837],{"class":3836},[3138,16372,16373,16375,16377,16380],{"class":3136,"line":3755},[3138,16374,3849],{"class":3832},[3138,16376,3852],{"class":3836},[3138,16378,16379],{"class":3855},"postgres:16.2-alpine",[3138,16381,16382],{"class":3697},"  # Конкретна версія\n",[3416,16384],{},[3109,16386,16388],{"id":16387},"резюме","Резюме",[3114,16390,16391],{},"Docker Compose — це потужний інструмент для управління multi-container застосунками, що значно спрощує розробку та deployment.",[3114,16393,16394],{},[3120,16395,3440],{},[3442,16397,16398,16403,16408,16413,16419],{},[3328,16399,16400,16402],{},[3120,16401,3448],{}," — описуєте архітектуру у YAML, Compose керує lifecycle",[3328,16404,16405,16407],{},[3120,16406,3409],{}," — єдине джерело правди для всієї архітектури застосунку",[3328,16409,16410,16412],{},[3120,16411,6359],{}," — логічні компоненти застосунку (backend, db, cache), кожен може мати кілька реплік",[3328,16414,16415,16418],{},[3120,16416,16417],{},"Networks"," — ізоляція та комунікація між сервісами через Docker DNS",[3328,16420,16421,16424],{},[3120,16422,16423],{},"Volumes"," — persistent storage для даних, що мають пережити перезапуск контейнерів",[3114,16426,16427],{},[3120,16428,16429],{},"Основні команди:",[3325,16431,16432,16437,16442,16448,16453],{},[3328,16433,16434,16436],{},[3335,16435,4929],{}," — запустити застосунок у background",[3328,16438,16439,16441],{},[3335,16440,3477],{}," — зупинити та видалити контейнери",[3328,16443,16444,16447],{},[3335,16445,16446],{},"docker compose logs -f"," — переглянути логи в реальному часі",[3328,16449,16450,16452],{},[3335,16451,11636],{}," — статус сервісів",[3328,16454,16455,11787],{},[3335,16456,16457],{},"docker compose exec \u003Cservice> \u003Ccommand>",[3114,16459,16460],{},[3120,16461,16462],{},"Просунуті можливості:",[3325,16464,16465,16471,16477,16483,16489],{},[3328,16466,16467,16470],{},[3120,16468,16469],{},"Override files"," — різні конфігурації для development/production",[3328,16472,16473,16476],{},[3120,16474,16475],{},"Profiles"," — умовний запуск сервісів (debug-інструменти, monitoring)",[3328,16478,16479,16482],{},[3120,16480,16481],{},"Health checks"," — чекати готовності залежних сервісів",[3328,16484,16485,16488],{},[3120,16486,16487],{},"Extends"," — повторне використання конфігурації",[3328,16490,16491,16494],{},[3120,16492,16493],{},"Secrets"," — безпечне управління чутливими даними",[3114,16496,16497],{},[3120,16498,16499],{},"Найкращі практики:",[3325,16501,16502,16508,16511,16514,16520,16526],{},[3328,16503,16504,16505,16507],{},"Використовуйте ",[3335,16506,7274],{}," для конфігурації, не хардкодьте значення",[3328,16509,16510],{},"Розділяйте конфігурацію за середовищами через override-файли",[3328,16512,16513],{},"Додавайте health checks для критичних сервісів",[3328,16515,16516,16517],{},"Обмежуйте ресурси у production через ",[3335,16518,16519],{},"deploy.resources",[3328,16521,16522,16523,4250],{},"Версіонуйте образи (не використовуйте ",[3335,16524,16525],{},"latest",[3328,16527,16528],{},"Документуйте архітектуру у README та коментарях",[3114,16530,16531],{},[3120,16532,16533],{},"Що далі:",[3114,16535,16536,16537,16540],{},"У наступній статті ми розглянемо ",[3120,16538,16539],{},"Docker у production"," — best practices для deployment, моніторинг, логування, security hardening, та інтеграцію з CI/CD pipelines. Ви навчитеся готувати Docker-застосунки до реального production-середовища з високими вимогами до надійності та безпеки.",[3416,16542],{},[3109,16544,16546],{"id":16545},"практичні-завдання","Практичні завдання",[3423,16548,16550],{"id":16549},"рівень-1-базове-розуміння","Рівень 1: Базове розуміння",[3114,16552,16553],{},[3120,16554,16555],{},"Завдання 1.1: Перший docker-compose.yml",[3114,16557,16299,16558,16560],{},[3335,16559,3409],{}," для простого веб-сервера Nginx.",[3114,16562,16563],{},[3120,16564,16565],{},"Вимоги:",[3325,16567,16568,16573,16582],{},[3328,16569,16570,16571],{},"Використати образ ",[3335,16572,4172],{},[3328,16574,16575,16576,16578,16579,16581],{},"Пробросити порт ",[3335,16577,3987],{}," хоста на ",[3335,16580,3983],{}," контейнера",[3328,16583,16584,16585,4687,16588],{},"Змонтувати локальну директорію ",[3335,16586,16587],{},"./html",[3335,16589,16590],{},"/usr/share/nginx/html",[3114,16592,16593],{},[3120,16594,3996],{},[3670,16596,16598],{"className":3672,"code":16597,"language":3674,"meta":3675,"style":3675},"echo \"\u003Ch1>Hello from Compose!\u003C/h1>\" > html/index.html\ndocker compose up -d\ncurl http://localhost:8080\n",[3335,16599,16600,16612,16622],{"__ignoreMap":3675},[3138,16601,16602,16604,16607,16609],{"class":3136,"line":3680},[3138,16603,15749],{"class":3683},[3138,16605,16606],{"class":3687}," \"\u003Ch1>Hello from Compose!\u003C/h1>\"",[3138,16608,10962],{"class":3836},[3138,16610,16611],{"class":3687},"html/index.html\n",[3138,16613,16614,16616,16618,16620],{"class":3136,"line":3694},[3138,16615,3684],{"class":3683},[3138,16617,3688],{"class":3687},[3138,16619,4919],{"class":3687},[3138,16621,4922],{"class":3725},[3138,16623,16624,16626],{"class":3136,"line":3755},[3138,16625,4011],{"class":3683},[3138,16627,4014],{"class":3687},[3114,16629,16630],{},[3120,16631,16632],{},"Завдання 1.2: Multi-container з базою даних",[3114,16634,16635],{},"Створіть застосунок з двома сервісами: WordPress та MySQL.",[3114,16637,16638],{},[3120,16639,16565],{},[3325,16641,16642,16645,16648,16651],{},[3328,16643,16644],{},"MySQL з environment variables для налаштування",[3328,16646,16647],{},"WordPress підключений до MySQL через Docker DNS",[3328,16649,16650],{},"Named volumes для persistent storage",[3328,16652,16653,16654],{},"WordPress доступний на ",[3335,16655,16656],{},"http://localhost:8080",[3114,16658,16659,16662],{},[3120,16660,16661],{},"Підказка:"," Використайте приклад з розділу \"Перший docker-compose.yml\".",[3114,16664,16665],{},[3120,16666,16667],{},"Завдання 1.3: Логи та діагностика",[3114,16669,16670],{},"Запустіть застосунок з попереднього завдання та:",[3325,16672,16673,16676,16679],{},[3328,16674,16675],{},"Переглянути логи MySQL",[3328,16677,16678],{},"Виконати SQL-запит всередині контейнера MySQL",[3328,16680,16681],{},"Перезапустити лише WordPress (без MySQL)",[3670,16683,16685],{"className":3672,"code":16684,"language":3674,"meta":3675,"style":3675},"docker compose logs db\ndocker compose exec db mysql -u root -p\ndocker compose restart wordpress\n",[3335,16686,16687,16697,16717],{"__ignoreMap":3675},[3138,16688,16689,16691,16693,16695],{"class":3136,"line":3680},[3138,16690,3684],{"class":3683},[3138,16692,3688],{"class":3687},[3138,16694,5048],{"class":3687},[3138,16696,11470],{"class":3687},[3138,16698,16699,16701,16703,16705,16707,16710,16712,16714],{"class":3136,"line":3694},[3138,16700,3684],{"class":3683},[3138,16702,3688],{"class":3687},[3138,16704,11806],{"class":3687},[3138,16706,5745],{"class":3687},[3138,16708,16709],{"class":3687}," mysql",[3138,16711,11859],{"class":3725},[3138,16713,11862],{"class":3687},[3138,16715,16716],{"class":3725}," -p\n",[3138,16718,16719,16721,16723,16725],{"class":3136,"line":3755},[3138,16720,3684],{"class":3683},[3138,16722,3688],{"class":3687},[3138,16724,11622],{"class":3687},[3138,16726,5051],{"class":3687},[3416,16728],{},[3423,16730,16732],{"id":16731},"рівень-2-практичне-застосування","Рівень 2: Практичне застосування",[3114,16734,16735],{},[3120,16736,16737],{},"Завдання 2.1: Development environment з hot-reload",[3114,16739,16740],{},"Створіть development-середовище для Node.js застосунку з hot-reload.",[3114,16742,16743],{},[3120,16744,16745],{},"Структура:",[3670,16747,16750],{"className":16748,"code":16749,"language":5082},[5080],"myapp/\n├── docker-compose.yml\n├── app/\n│   ├── package.json\n│   └── index.js\n",[3335,16751,16749],{"__ignoreMap":3675},[3114,16753,16754],{},[3120,16755,16565],{},[3325,16757,16758,16763,16771,16777],{},[3328,16759,16570,16760],{},[3335,16761,16762],{},"node:20-alpine",[3328,16764,16765,16766,4687,16768,16770],{},"Змонтувати ",[3335,16767,6152],{},[3335,16769,5565],{}," у контейнері",[3328,16772,16773,16774,16776],{},"Запустити ",[3335,16775,10119],{}," (з nodemon для hot-reload)",[3328,16778,3980,16779,16782],{},[3335,16780,16781],{},"3000"," доступний на хості",[3114,16784,16785],{},[3120,16786,16787],{},"index.js:",[3670,16789,16791],{"className":5616,"code":16790,"language":5618,"meta":3675,"style":3675},"const express = require('express');\nconst app = express();\n\napp.get('/', (req, res) => {\n  res.send('Hello from Docker Compose!');\n});\n\napp.listen(3000, '0.0.0.0', () => {\n  console.log('Server running on port 3000');\n});\n",[3335,16792,16793,16812,16825,16829,16859,16876,16880,16884,16908,16925],{"__ignoreMap":3675},[3138,16794,16795,16797,16800,16802,16805,16807,16810],{"class":3136,"line":3680},[3138,16796,5664],{"class":3725},[3138,16798,16799],{"class":5667}," express",[3138,16801,5671],{"class":3836},[3138,16803,16804],{"class":3683},"require",[3138,16806,5704],{"class":3836},[3138,16808,16809],{"class":3687},"'express'",[3138,16811,6345],{"class":3836},[3138,16813,16814,16816,16818,16820,16823],{"class":3136,"line":3694},[3138,16815,5664],{"class":3725},[3138,16817,6422],{"class":5667},[3138,16819,5671],{"class":3836},[3138,16821,16822],{"class":3683},"express",[3138,16824,6434],{"class":3836},[3138,16826,16827],{"class":3136,"line":3755},[3138,16828,3772],{"emptyLinePlaceholder":3771},[3138,16830,16831,16833,16835,16837,16839,16841,16844,16847,16849,16852,16855,16857],{"class":3136,"line":3768},[3138,16832,6460],{"class":5629},[3138,16834,3394],{"class":3836},[3138,16836,5770],{"class":3683},[3138,16838,5704],{"class":3836},[3138,16840,5775],{"class":3687},[3138,16842,16843],{"class":3836},", (",[3138,16845,16846],{"class":5629},"req",[3138,16848,4272],{"class":3836},[3138,16850,16851],{"class":5629},"res",[3138,16853,16854],{"class":3836},") ",[3138,16856,5786],{"class":3725},[3138,16858,5789],{"class":3836},[3138,16860,16861,16864,16866,16869,16871,16874],{"class":3136,"line":3775},[3138,16862,16863],{"class":5629},"  res",[3138,16865,3394],{"class":3836},[3138,16867,16868],{"class":3683},"send",[3138,16870,5704],{"class":3836},[3138,16872,16873],{"class":3687},"'Hello from Docker Compose!'",[3138,16875,6345],{"class":3836},[3138,16877,16878],{"class":3136,"line":3781},[3138,16879,6721],{"class":3836},[3138,16881,16882],{"class":3136,"line":4165},[3138,16883,3772],{"emptyLinePlaceholder":3771},[3138,16885,16886,16888,16890,16892,16894,16896,16898,16901,16904,16906],{"class":3136,"line":4178},[3138,16887,6460],{"class":5629},[3138,16889,3394],{"class":3836},[3138,16891,5904],{"class":3683},[3138,16893,5704],{"class":3836},[3138,16895,16781],{"class":5389},[3138,16897,4272],{"class":3836},[3138,16899,16900],{"class":3687},"'0.0.0.0'",[3138,16902,16903],{"class":3836},", () ",[3138,16905,5786],{"class":3725},[3138,16907,5789],{"class":3836},[3138,16909,16910,16913,16915,16918,16920,16923],{"class":3136,"line":4189},[3138,16911,16912],{"class":5629},"  console",[3138,16914,3394],{"class":3836},[3138,16916,16917],{"class":3683},"log",[3138,16919,5704],{"class":3836},[3138,16921,16922],{"class":3687},"'Server running on port 3000'",[3138,16924,6345],{"class":3836},[3138,16926,16927],{"class":3136,"line":4377},[3138,16928,6721],{"class":3836},[3114,16930,16931,16933,16934,16937],{},[3120,16932,3996],{}," Змініть текст у ",[3335,16935,16936],{},"index.js"," — сервер має автоматично перезапуститися.",[3114,16939,16940],{},[3120,16941,16942],{},"Завдання 2.2: Мережева ізоляція",[3114,16944,16945],{},"Створіть застосунок з трьома сервісами та двома мережами:",[3325,16947,16948,16954],{},[3328,16949,16950,16953],{},[3120,16951,16952],{},"frontend-net:"," nginx, backend",[3328,16955,16956,16959],{},[3120,16957,16958],{},"backend-net:"," backend, postgres",[3114,16961,16962],{},[3120,16963,16565],{},[3325,16965,16966,16969,16972],{},[3328,16967,16968],{},"Nginx не може підключитися до postgres",[3328,16970,16971],{},"Backend може підключитися до обох",[3328,16973,16974],{},"Використати health checks для postgres",[3114,16976,16977],{},[3120,16978,3996],{},[3670,16980,16982],{"className":3672,"code":16981,"language":3674,"meta":3675,"style":3675},"docker compose exec nginx ping postgres\n# Має бути: bad address 'postgres'\n\ndocker compose exec backend ping postgres\n# Має працювати\n",[3335,16983,16984,17001,17006,17010,17024],{"__ignoreMap":3675},[3138,16985,16986,16988,16990,16992,16995,16998],{"class":3136,"line":3680},[3138,16987,3684],{"class":3683},[3138,16989,3688],{"class":3687},[3138,16991,11806],{"class":3687},[3138,16993,16994],{"class":3687}," nginx",[3138,16996,16997],{"class":3687}," ping",[3138,16999,17000],{"class":3687}," postgres\n",[3138,17002,17003],{"class":3136,"line":3694},[3138,17004,17005],{"class":3697},"# Має бути: bad address 'postgres'\n",[3138,17007,17008],{"class":3136,"line":3755},[3138,17009,3772],{"emptyLinePlaceholder":3771},[3138,17011,17012,17014,17016,17018,17020,17022],{"class":3136,"line":3768},[3138,17013,3684],{"class":3683},[3138,17015,3688],{"class":3687},[3138,17017,11806],{"class":3687},[3138,17019,11467],{"class":3687},[3138,17021,16997],{"class":3687},[3138,17023,17000],{"class":3687},[3138,17025,17026],{"class":3136,"line":3775},[3138,17027,17028],{"class":3697},"# Має працювати\n",[3114,17030,17031],{},[3120,17032,17033],{},"Завдання 2.3: Override files",[3114,17035,17036],{},"Створіть базову конфігурацію та два override-файли: development та production.",[3114,17038,17039],{},[3120,17040,16565],{},[3325,17042,17043,17048,17053],{},[3328,17044,17045,17047],{},[3335,17046,3409],{}," — базова конфігурація (образи, мережі)",[3328,17049,17050,17052],{},[3335,17051,13167],{}," — development (bind mounts, проброс портів БД)",[3328,17054,17055,17057],{},[3335,17056,9011],{}," — production (restart policies, resource limits)",[3114,17059,17060],{},[3120,17061,3996],{},[3670,17063,17065],{"className":3672,"code":17064,"language":3674,"meta":3675,"style":3675},"# Development\ndocker compose up -d\n\n# Production\ndocker compose -f docker-compose.yml -f docker-compose.prod.yml up -d\n",[3335,17066,17067,17072,17082,17086,17091],{"__ignoreMap":3675},[3138,17068,17069],{"class":3136,"line":3680},[3138,17070,17071],{"class":3697},"# Development\n",[3138,17073,17074,17076,17078,17080],{"class":3136,"line":3694},[3138,17075,3684],{"class":3683},[3138,17077,3688],{"class":3687},[3138,17079,4919],{"class":3687},[3138,17081,4922],{"class":3725},[3138,17083,17084],{"class":3136,"line":3755},[3138,17085,3772],{"emptyLinePlaceholder":3771},[3138,17087,17088],{"class":3136,"line":3768},[3138,17089,17090],{"class":3697},"# Production\n",[3138,17092,17093,17095,17097,17099,17101,17103,17105,17107],{"class":3136,"line":3775},[3138,17094,3684],{"class":3683},[3138,17096,3688],{"class":3687},[3138,17098,8872],{"class":3725},[3138,17100,8972],{"class":3687},[3138,17102,8872],{"class":3725},[3138,17104,8977],{"class":3687},[3138,17106,4919],{"class":3687},[3138,17108,4922],{"class":3725},[3416,17110],{},[3423,17112,17114],{"id":17113},"рівень-3-архітектура-та-оптимізація","Рівень 3: Архітектура та оптимізація",[3114,17116,17117],{},[3120,17118,17119],{},"Завдання 3.1: Full-stack застосунок",[3114,17121,17122],{},"Створіть повноцінний full-stack застосунок з усіма компонентами:",[3114,17124,17125],{},[3120,17126,17127],{},"Компоненти:",[3442,17129,17130,17133,17136,17139,17142],{},[3328,17131,17132],{},"Frontend (React або Vue)",[3328,17134,17135],{},"Backend (Node.js Express або .NET)",[3328,17137,17138],{},"Database (PostgreSQL)",[3328,17140,17141],{},"Cache (Redis)",[3328,17143,17144],{},"Reverse Proxy (Nginx)",[3114,17146,17147],{},[3120,17148,16565],{},[3325,17150,17151,17154,17157,17160,17163],{},[3328,17152,17153],{},"Три мережі: frontend-net, backend-net, cache-net",[3328,17155,17156],{},"Health checks для всіх stateful сервісів",[3328,17158,17159],{},"Named volumes для persistent data",[3328,17161,17162],{},"Development override з hot-reload",[3328,17164,17165],{},"Production override з resource limits та restart policies",[3114,17167,17168],{},[3120,17169,17170],{},"Архітектура:",[3670,17172,17175],{"className":17173,"code":17174,"language":5082},[5080],"Internet → Nginx → Frontend (React)\n                ↓\n              Backend (API) → PostgreSQL\n                ↓\n              Redis (Cache)\n",[3335,17176,17174],{"__ignoreMap":3675},[3114,17178,17179],{},[3120,17180,17181],{},"Завдання 3.2: CI/CD інтеграція",[3114,17183,17184],{},"Створіть GitHub Actions workflow для автоматичного deployment.",[3114,17186,17187],{},[3120,17188,16565],{},[3325,17190,17191,17196,17199,17202],{},[3328,17192,17193,17194],{},"Build образів через ",[3335,17195,11914],{},[3328,17197,17198],{},"Push образів до Docker Hub",[3328,17200,17201],{},"Deploy на сервер через SSH",[3328,17203,17204],{},"Використання secrets для credentials",[3114,17206,17207],{},[3120,17208,17209],{},"Завдання 3.3: Моніторинг та логування",[3114,17211,17212],{},"Додайте до застосунку з завдання 3.1 моніторинг та централізоване логування.",[3114,17214,17215],{},[3120,17216,17217],{},"Додаткові сервіси:",[3325,17219,17220,17223,17226,17229],{},[3328,17221,17222],{},"Prometheus (збір метрик)",[3328,17224,17225],{},"Grafana (візуалізація)",[3328,17227,17228],{},"Loki (логування)",[3328,17230,17231],{},"Promtail (збір логів)",[3114,17233,17234],{},[3120,17235,16565],{},[3325,17237,17238,17244,17247,17250],{},[3328,17239,17240,17241,4250],{},"Використати profiles для моніторингу (",[3335,17242,17243],{},"--profile monitoring",[3328,17245,17246],{},"Налаштувати Prometheus для scraping метрик з backend",[3328,17248,17249],{},"Налаштувати Grafana dashboard",[3328,17251,17252],{},"Централізувати логи всіх сервісів у Loki",[3114,17254,17255],{},[3120,17256,12645],{},[3670,17258,17260],{"className":3672,"code":17259,"language":3674,"meta":3675,"style":3675},"docker compose --profile monitoring up -d\n",[3335,17261,17262],{"__ignoreMap":3675},[3138,17263,17264,17266,17268,17270,17272,17274],{"class":3136,"line":3680},[3138,17265,3684],{"class":3683},[3138,17267,3688],{"class":3687},[3138,17269,6107],{"class":3725},[3138,17271,12712],{"class":3687},[3138,17273,4919],{"class":3687},[3138,17275,4922],{"class":3725},[3416,17277],{},[3412,17279,17280,17282],{},[3120,17281,16661],{}," Для завдань рівня 3 використовуйте приклад \"Реальний приклад: Full-stack застосунок\" як основу та розширюйте його.",[17284,17285,17286],"style",{},"html pre.shiki code .s8Opu, html code.shiki .s8Opu{--shiki-light:#795E26;--shiki-default:#DCDCAA;--shiki-dark:#DCDCAA}html pre.shiki code .sbdoH, html code.shiki .sbdoH{--shiki-light:#A31515;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .spJ8K, html code.shiki .spJ8K{--shiki-light:#008000;--shiki-default:#6A9955;--shiki-dark:#6A9955}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .su1O8, html code.shiki .su1O8{--shiki-light:#0000FF;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .sKtos, html code.shiki .sKtos{--shiki-light:#800000;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .sHH4Y, html code.shiki .sHH4Y{--shiki-light:#000000;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .su9tN, html code.shiki .su9tN{--shiki-light:#0000FF;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .sJj4R, html code.shiki .sJj4R{--shiki-light:#098658;--shiki-default:#B5CEA8;--shiki-dark:#B5CEA8}html pre.shiki code .sCDza, html code.shiki .sCDza{--shiki-light:#AF00DB;--shiki-default:#CE92A4;--shiki-dark:#CE92A4}html pre.shiki code .siwwj, html code.shiki .siwwj{--shiki-light:#001080;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .s-QsJ, html code.shiki .s-QsJ{--shiki-light:#0070C1;--shiki-default:#4FC1FF;--shiki-dark:#4FC1FF}html pre.shiki code .sLwNe, html code.shiki .sLwNe{--shiki-light:#0451A5;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .sN1BT, html code.shiki .sN1BT{--shiki-light:#267F99;--shiki-default:#4EC9B0;--shiki-dark:#4EC9B0}html pre.shiki code .sD7JJ, html code.shiki .sD7JJ{--shiki-light:#000000FF;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .sjcCO, html code.shiki .sjcCO{--shiki-light:#EE0000;--shiki-default:#D7BA7D;--shiki-dark:#D7BA7D}",{"title":3675,"searchDepth":3694,"depth":3694,"links":17288},[17289,17290,17295,17299,17316,17325,17331,17337,17346,17350,17360,17361],{"id":3111,"depth":3694,"text":3112},{"id":3420,"depth":3694,"text":3421,"children":17291},[17292,17293,17294],{"id":3425,"depth":3755,"text":3426},{"id":3489,"depth":3755,"text":3490},{"id":3652,"depth":3755,"text":3653},{"id":3804,"depth":3694,"text":3805,"children":17296},[17297,17298],{"id":3808,"depth":3755,"text":3809},{"id":4105,"depth":3755,"text":4106},{"id":4280,"depth":3694,"text":4281,"children":17300},[17301,17302,17303,17304,17305,17306,17307,17308,17309,17310,17311,17312,17313,17314,17315],{"id":4287,"depth":3755,"text":4288},{"id":4899,"depth":3755,"text":4900},{"id":5090,"depth":3755,"text":5091},{"id":6034,"depth":3755,"text":6035},{"id":6068,"depth":3755,"text":6069},{"id":6092,"depth":3755,"text":6093},{"id":6162,"depth":3755,"text":6163},{"id":7384,"depth":3755,"text":7385},{"id":7413,"depth":3755,"text":6035},{"id":7445,"depth":3755,"text":7446},{"id":7472,"depth":3755,"text":7473},{"id":7538,"depth":3755,"text":7539},{"id":7591,"depth":3755,"text":7592},{"id":9044,"depth":3755,"text":9045},{"id":9048,"depth":3755,"text":9045},{"id":9152,"depth":3694,"text":9153,"children":17317},[17318,17319,17320,17321,17322,17323,17324],{"id":9156,"depth":3755,"text":9157},{"id":9479,"depth":3755,"text":9480},{"id":9771,"depth":3755,"text":9772},{"id":10123,"depth":3755,"text":10124},{"id":10538,"depth":3755,"text":10539},{"id":10751,"depth":3755,"text":10752},{"id":11254,"depth":3755,"text":11255},{"id":11378,"depth":3694,"text":11379,"children":17326},[17327,17328,17329,17330],{"id":11382,"depth":3755,"text":11383},{"id":11627,"depth":3755,"text":11628},{"id":11905,"depth":3755,"text":11906},{"id":12090,"depth":3755,"text":3556},{"id":12257,"depth":3694,"text":12258,"children":17332},[17333,17334,17335,17336],{"id":12261,"depth":3755,"text":12262},{"id":12482,"depth":3755,"text":12483},{"id":12756,"depth":3755,"text":12757},{"id":13208,"depth":3755,"text":13209},{"id":13428,"depth":3694,"text":13429,"children":17338},[17339,17340,17341,17342,17343,17344,17345],{"id":13539,"depth":3755,"text":13540},{"id":13593,"depth":3755,"text":13594},{"id":14199,"depth":3755,"text":14200},{"id":14480,"depth":3755,"text":14481},{"id":14752,"depth":3755,"text":9749},{"id":14818,"depth":3755,"text":14819},{"id":15009,"depth":3755,"text":15010},{"id":15402,"depth":3694,"text":15403,"children":17347},[17348,17349],{"id":15406,"depth":3755,"text":15407},{"id":15786,"depth":3755,"text":15787},{"id":15920,"depth":3694,"text":15921,"children":17351},[17352,17353,17354,17355,17356,17357,17358,17359],{"id":15924,"depth":3755,"text":15925},{"id":16016,"depth":3755,"text":16017},{"id":16023,"depth":3755,"text":16024},{"id":16095,"depth":3755,"text":16096},{"id":16173,"depth":3755,"text":16174},{"id":16240,"depth":3755,"text":16241},{"id":16295,"depth":3755,"text":16296},{"id":16315,"depth":3755,"text":16316},{"id":16387,"depth":3694,"text":16388},{"id":16545,"depth":3694,"text":16546,"children":17362},[17363,17364,17365],{"id":16549,"depth":3755,"text":16550},{"id":16731,"depth":3755,"text":16732},{"id":17113,"depth":3755,"text":17114},"Декларативне управління multi-container застосунками через docker-compose.yml","md",null,{},{"title":2727,"description":17366},"K6ezDVnvGoBiH3P9JBi300VAQXbBcSBPVvJdJD94kTI",[17373,17375],{"title":2723,"path":2724,"stem":2725,"description":17374,"children":-1},"Передача конфігурації в Docker-контейнери — ENV, env files, secrets, 12-Factor App",{"title":2731,"path":2732,"stem":2733,"description":17376,"children":-1},"Розширена робота з Docker Compose — залежності, мережі, томи, profiles та orchestration patterns",1778489430275]