# GO

# Информация по append в Go

`append` — одна из самых важных встроенных функций в Go для работы со слайсами. Ниже рассмотрены все возможные способы использования, примеры и пояснения.

---

## 🔧 Базовый синтаксис

```go
append(slice, elems...)
```

- `slice` — срез, к которому добавляем
- `elems...` — один или несколько элементов **или другой срез**

---

## ✅ Варианты использования

### 1. Добавить один элемент

```go
nums := []int{1, 2}
nums = append(nums, 3)
// → [1 2 3]
```

---

### 2. Добавить несколько элементов

```go
nums = append(nums, 4, 5, 6)
// → [1 2 3 4 5 6]
```

---

### 3. Добавить другой срез

```go
more := []int{7, 8}
nums = append(nums, more...) // обязательно `...`!
```

---

### 4. Добавить пустой срез

```go
nums = append(nums, []int{}...) // всё ещё [1 2 3 4 5 6 7 8]
```

---

### 5. Добавить к `nil`-срезу

```go
var s []int
s = append(s, 10) // работает, даже если s == nil
```

---

### 6. Удалить элемент (по индексу)

```go
i := 2
s = append(s[:i], s[i+1:]...)
```

---

## 📐 Как работает `append`

- Если у слайса **есть свободная capacity** — элементы просто добавляются.
- Если нет — Go **создаёт новый массив**, копирует старые значения и добавляет новые.
- Поэтому `append` может **изменить underlying array**.

---

## 🛠 Трюки с `append`

### 🔁 Объединение слайсов

```go
a := []string{"a", "b"}
b := []string{"c", "d"}
a = append(a, b...) // → [a b c d]
```

---

### 🪓 Удаление последнего элемента

```go
s = s[:len(s)-1]
```

---

## ⚠️ Важно помнить

- `append` **всегда возвращает новый слайс** (возможно тот же, возможно новый)
- Обязательно `...`, если добавляешь другой срез: `append(s, other...)`
- Работает **только со слайсами**, не с массивами

---

## 🧠 Сводка

<table id="bkmrk-%D0%98%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80"><thead><tr><th id="bkmrk-%D0%98%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5">Использование</th><th id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80">Пример</th></tr></thead><tbody><tr><td>Один элемент</td><td>`append(s, 10)`</td></tr><tr><td>Несколько элементов</td><td>`append(s, 1, 2, 3)`</td></tr><tr><td>Добавить срез</td><td>`append(s, other...)`</td></tr><tr><td>Удалить элемент</td><td>`append(s[:i], s[i+1:]...)`</td></tr><tr><td>Работать с `nil`-срезом</td><td>`append(nil, 1)`</td></tr></tbody></table>

# Как устроен планировщик горутин в Go: G-M-P модель

Go использует уникальную модель управления конкурентностью — **G-M-P** (Goroutine, Machine, Processor), которая обеспечивает масштабируемость, высокую производительность и лёгкость работы с параллелизмом.

---

## <a id="bkmrk--1"></a>🧠 Компоненты Go-планировщика

<table class="table table-striped table-bordered" id="bkmrk-%D0%9E%D0%B1%D0%BE%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-%D0%9D%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5"><thead><tr><th>Обозначение</th><th>Название</th><th>Что это такое</th></tr></thead><tbody><tr><td>**G**</td><td>Goroutine</td><td>Логическая единица выполнения (код + стек)</td></tr><tr><td>**M**</td><td>Machine (Thread)</td><td>Системный поток (OS thread)</td></tr><tr><td>**P**</td><td>Processor</td><td>Планировщик, управляющий выполнением G</td></tr></tbody></table>

---

## <a id="bkmrk--3"></a>🔄 Как это работает?

- **M (Thread)** — физический поток, выполняющий код
- **P (Processor)** — абстрактный процессор: даёт M-у задачу (G)
- **G (Goroutine)** — логическая задача, которую нужно выполнить

> M нужен P, чтобы выполнять G. Без P поток (M) простаивает.

---

## <a id="bkmrk--5"></a>🔁 Модель в действии

При запуске:

```
runtime.GOMAXPROCS(4)

```

Ты создаёшь 4 **P (процессора)**. Это означает, что Go может **одновременно** запускать до 4 горутин **в реальном параллелизме**.

---

## <a id="bkmrk--7"></a>🧬 Визуально:

```
+-------------+        +----------+        +------------------+
| Goroutine G |   -->  |   P      |  -->   | Thread M (OS)    |
+-------------+        +----------+        +------------------+
       ^                     |
       |_____________________|
         Work stealing (если P простаивает)

```

---

## <a id="bkmrk--9"></a>⏳ Work-stealing

Если один P (процессор) простаивает, он может “украсть” G из очереди другого P. Это делает систему ещё более гибкой и **минимизирует простои**.

---

## <a id="bkmrk--11"></a>📌 Примеры поведения

<table class="table table-striped table-bordered" id="bkmrk-%D0%A1%D1%86%D0%B5%D0%BD%D0%B0%D1%80%D0%B8%D0%B9-%D0%9A%D0%B0%D0%BA-%D1%81%D0%B5%D0%B1%D1%8F-%D0%B2%D0%B5"><thead><tr><th>Сценарий</th><th>Как себя ведёт Go</th></tr></thead><tbody><tr><td>1 млн горутин</td><td>Go спокойно справится</td></tr><tr><td>Ожидание по `time.Sleep`</td><td>P отходит от M и отдаёт другим</td></tr><tr><td>Блокировка I/O</td><td>M блокируется, но P берёт новый M</td></tr><tr><td>`runtime.GOMAXPROCS(n)`</td><td>Ограничивает кол-во одновременно исполняемых G</td></tr></tbody></table>

---

## <a id="bkmrk--13"></a>✅ Вывод:

Go-планировщик:

- работает по **G-M-P** модели
- очень эффективно управляет тысячами горутин
- масштабируется лучше, чем системы, построенные на потоках ОС
- делает конкурентное программирование простым и надёжным

# Сравнение: Мьютексы и Каналы в Go

Go предлагает два подхода к синхронизации и обмену данными между горутинами:

- 🔒 **Мьютексы (Mutex)** — синхронизация доступа к общей памяти
- 📬 **Каналы (Channels)** — передача данных без разделяемой памяти

---

## <a id="bkmrk--1"></a>🔒 Мьютексы (`sync.Mutex`)

Мьютекс позволяет **гарантировать, что только одна горутина** в момент времени имеет доступ к критической секции кода.

### <a id="bkmrk--2"></a>✅ Пример:

```
import "sync"

type Counter struct {
    m  sync.Mutex
    v  int
}

func (c *Counter) Inc() {
    c.m.Lock()
    c.v++
    c.m.Unlock()
}

```

### <a id="bkmrk--3"></a>🧠 Когда использовать мьютекс:

- Множественные горутины читают/пишут переменную
- Работа с `map`, `slice` без гонки данных
- Критический код, который нельзя выполнять одновременно

---

## <a id="bkmrk--5"></a>📬 Каналы (`chan`)

Каналы передают данные между горутинами. Это позволяет **избежать прямого доступа к разделяемой памяти**.

### <a id="bkmrk--6"></a>✅ Пример:

```
func worker(jobs <-chan int, results chan<- int) {
    for job := range jobs {
        results <- job * 2
    }
}

func main() {
    jobs := make(chan int, 5)
    results := make(chan int, 5)

    go worker(jobs, results)

    jobs <- 1
    jobs <- 2
    close(jobs)

    fmt.Println(<-results)
    fmt.Println(<-results)
}

```

### <a id="bkmrk--7"></a>🧠 Когда использовать каналы:

- Потоковая обработка данных
- Логика “продюсер/консюмер”
- Синхронизация начала/окончания работы
- Архитектура “fan-in”, “fan-out”

---

## <a id="bkmrk--9"></a>⚖️ Сравнение

<table class="table table-striped table-bordered" id="bkmrk-%D0%9A%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B9-%D0%9C%D1%8C%D1%8E%D1%82%D0%B5%D0%BA%D1%81%D1%8B-%D0%9A%D0%B0"><thead><tr><th>Критерий</th><th>Мьютексы</th><th>Каналы</th></tr></thead><tbody><tr><td>Принцип</td><td>Общая память</td><td>Сообщения</td></tr><tr><td>Сложность</td><td>Низкая, но с рисками</td><td>Чище, но требует дизайна</td></tr><tr><td>Поддержка `pprof`</td><td>Да (через `mutex`)</td><td>Частично (горутины)</td></tr><tr><td>Предотвращение гонок</td><td>Да</td><td>Да</td></tr><tr><td>Применение</td><td>Быстрый доступ</td><td>Асинхронные операции</td></tr></tbody></table>

---

## <a id="bkmrk--11"></a>🧪 Рекомендация от Go-разработчиков

> **“Не общайся через общую память — делись памятью через общение.”**

Используй **каналы**, если можешь выразить логику через них, и **мьютексы**, когда нужна низкоуровневая производительность.

---

## <a id="bkmrk--13"></a>📌 Итог

| Хочешь максимальную скорость | Используй `sync.Mutex` |  
| Хочешь безопасный обмен | Используй `chan` |  
| Хочешь лучшее из двух миров | Используй `sync.Map`, `sync.Once`, `sync.WaitGroup` при необходимости |

---

## <a id="bkmrk--15"></a>🧰 Дополнительные инструменты из `sync`

Go также предоставляет удобные примитивы для управления конкурентностью:

---

### <a id="bkmrk--17"></a>🔁 `sync.Once` — выполнить только один раз

Позволяет гарантировать, что определённый код будет выполнен **только один раз**, даже если вызывается из нескольких горутин.

```
var once sync.Once

func initConfig() {
    once.Do(func() {
        fmt.Println("Конфигурация инициализирована")
    })
}

```

Используется для ленивой инициализации, подключения к БД, загрузки конфигов.

---

### <a id="bkmrk--19"></a>👥 `sync.WaitGroup` — дождаться завершения горутин

Позволяет дождаться, пока все горутины завершатся.

```
var wg sync.WaitGroup

for i := 0; i < 5; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        fmt.Printf("Горутина %d завершена
", id)
    }(i)
}

wg.Wait()
fmt.Println("Все горутины завершились")

```

---

### <a id="bkmrk--21"></a>🗺️ `sync.Map` — потокобезопасная map

Альтернатива обычной `map`, не требующая ручной синхронизации.

```
var sm sync.Map

sm.Store("foo", 42)
val, ok := sm.Load("foo")
if ok {
    fmt.Println("Значение:", val)
}

sm.Range(func(key, value any) bool {
    fmt.Printf("%v = %v
", key, value)
    return true
})

```

Подходит для кэширования, счётчиков, безопасной общей памяти между горутинами.

---

## <a id="bkmrk--23"></a>📌 Вывод:

<table class="table table-striped table-bordered" id="bkmrk-%D0%98%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82-%D0%9D%D0%B0%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8"><thead><tr><th>Инструмент</th><th>Назначение</th></tr></thead><tbody><tr><td>`sync.Mutex`</td><td>Защита разделяемых данных</td></tr><tr><td>`sync.Once`</td><td>Инициализация кода только один раз</td></tr><tr><td>`sync.WaitGroup`</td><td>Ожидание завершения группы горутин</td></tr><tr><td>`sync.Map`</td><td>Потокобезопасный ассоциативный массив</td></tr></tbody></table>

# Поведение for range с каналами в Go

В Go `for v := range ch` используется для последовательного чтения из канала `ch`. Цикл завершится **только** после того, как:

- канал будет **закрыт**, и
- все значения из него будут **прочитаны**

---

## 🔁 Сценарии поведения

### 1. Канал пуст и **не закрыт**

```go
ch := make(chan string)

for v := range ch {
    fmt.Println(v)
}
```

📌 Итог: **блокировка навсегда**, цикл ждёт значения, но никто не пишет.

---

### 2. В канале есть одно значение, канал **не закрыт**

```go
ch := make(chan string)

go func() {
    ch <- "hello"
}()

for v := range ch {
    fmt.Println(v)
}
```

📌 Итог: `hello` выведется, затем цикл **зависнет**, ожидая следующего значения.

---

### 3. В канале есть значение, потом ещё одно, затем закрытие

```go
ch := make(chan string)

go func() {
    ch <- "one"
    time.Sleep(1 * time.Second)
    ch <- "two"
    close(ch)
}()

for v := range ch {
    fmt.Println(v)
}
```

📌 Итог:

- `one` будет прочитан
- цикл подождёт
- `two` будет прочитан
- после `close(ch)` — цикл завершится

---

## ✅ Правильное завершение горутины через канал и WaitGroup

```go
import (
    "fmt"
    "sync"
)

func worker(id int, jobs <-chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for job := range jobs {
        fmt.Printf("Worker %d processing job %d\n", id, job)
    }
}

func main() {
    jobs := make(chan int)
    var wg sync.WaitGroup

    for w := 1; w <= 2; w++ {
        wg.Add(1)
        go worker(w, jobs, &wg)
    }

    for j := 1; j <= 5; j++ {
        jobs <- j
    }

    close(jobs)  // 🔑 важно закрыть канал
    wg.Wait()    // 🔒 ждём завершения всех воркеров
}
```

---

## 🧠 Общее поведение `range` по каналам

<table id="bkmrk-%D0%A1%D1%86%D0%B5%D0%BD%D0%B0%D1%80%D0%B8%D0%B9-%D0%9F%D0%BE%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5-%D0%9A"><thead><tr><th id="bkmrk-%D0%A1%D1%86%D0%B5%D0%BD%D0%B0%D1%80%D0%B8%D0%B9">Сценарий</th><th id="bkmrk-%D0%9F%D0%BE%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5">Поведение</th></tr></thead><tbody><tr><td>Канал пуст и не закрыт</td><td>❌ Блокировка</td></tr><tr><td>Есть 1 значение, канал не закрыт</td><td>✅ Прочтёт 1, потом зависнет</td></tr><tr><td>Есть значения, канал закрыт после них</td><td>✅ Прочтёт все, завершит цикл</td></tr><tr><td>Канал закрыт сразу</td><td>✅ Завершит цикл (если пуст)</td></tr></tbody></table>

---

## 📌 Рекомендации

- ✅ **Закрывай канал**, если больше не будет отправок
- ✅ Используй `sync.WaitGroup`, чтобы ждать завершения горутин
- ⚠️ Избегай чтения из **открытых, но не используемых** каналов

# Пример потока данных в Clean Architecture на Go

## <a id="bkmrk-"></a>Предисловие

Clean Architecture (Чистая Архитектура), предложенная Робертом Мартином (Uncle Bob), — это подход к проектированию ПО, который подчеркивает разделение на слои с четкими зависимостями.

**Основная идея**: сделать систему независимой от фреймворков, UI, баз данных и внешних сервисов. Зависимости направлены “внутрь” — внешние слои зависят от внутренних, но не наоборот.

### <a id="bkmrk--1"></a>Ключевые слои Clean Architecture:

- **Доменный слой (Domain/Entities)**: Содержит бизнес-сущности и бизнес-правила. Это “ядро” системы, не зависящее от внешнего мира.
- **Прикладной слой (Application/Use Cases)**: Описывает сценарии использования — бизнес-логику, которая оркеструет сущности. Зависит от домена, но не от инфраструктуры.
- **Слой адаптеров (Adapters/Controllers, Presenters)**: Обеспечивает взаимодействие с внешним миром (например, HTTP, CLI). Преобразует внешние запросы в вызовы Use Cases.
- **Инфраструктурный слой (Infrastructure/Frameworks &amp; Drivers)**: Реализует детали, такие как доступ к БД, API-клиенты. Зависит от внутренних слоев через интерфейсы (инверсия зависимостей).

## <a id="bkmrk--2"></a>Практический пример: Создание пользователя

**Сценарий**: Пользователь регистрируется, отправляя HTTP POST-запрос на `/users` с JSON-телом:

```
{
  "name": "Alice Johnson",
  "email": "alice@example.com"
}

```

Приложение использует PostgreSQL как БД. Рассмотрим пошаговый поток данных:

[![image.png](https://lavelin.ru/uploads/images/gallery/2025-09/scaled-1680-/image.png)](https://lavelin.ru/uploads/images/gallery/2025-09/image.png)

## <a id="bkmrk--4"></a>Входящий поток: От запроса к сохранению в БД

### <a id="bkmrk--5"></a>1. HTTP запрос → Адаптер (HTTP Handler)

**Что происходит**: Адаптер получает сырой HTTP запрос и парсит его.

- **Входные данные**:
    
    
    - Метод: POST
    - Путь: `/users`
    - Тело: `{"name": "Alice Johnson", "email": "alice@example.com"}`
- **Логирование**:
    
    ```
    [Адаптер] Получен POST-запрос на /users
    [Адаптер] Парсинг JSON: name="Alice Johnson", email="alice@example.com"
    [Адаптер] Вызываю контроллер с name="Alice Johnson", email="alice@example.com"
    
    ```
- **Роль в архитектуре**: Внешний слой адаптеров — точка входа, зависящая от HTTP.

### <a id="bkmrk--6"></a>2. Адаптер → Контроллер

**Что происходит**: Контроллер валидирует данные.

- **Входные данные**: `name = "Alice Johnson"`, `email = "alice@example.com"` (валидные)
- **Логирование**:
    
    ```
    [Контроллер] Получены данные: name="Alice Johnson", email="alice@example.com"
    [Контроллер] Валидация пройдена
    [Контроллер] Вызываю UseCase для создания пользователя
    
    ```
- **Роль в архитектуре**: Часть прикладного слоя — координирует, но без глубокой логики.

### <a id="bkmrk--7"></a>3. Контроллер → UseCase

**Что происходит**: UseCase применяет бизнес-правила и проверяет уникальность.

- **Входные данные**:
    
    
    - Проверка: `FindByEmail("alice@example.com")` → `nil` (не существует)
    - Создание: `User{Name: "Alice Johnson", Email: "alice@example.com"}`
- **Логирование**:
    
    ```
    [UseCase] Запуск бизнес-логики для создания пользователя: name="Alice Johnson"
    [UseCase] Проверка существования: вызываю repo.FindByEmail("alice@example.com")
    [UseCase] Пользователь не найден - можно создавать
    [UseCase] Создаю модель User: {Name: "Alice Johnson", Email: "alice@example.com"}
    [UseCase] Вызываю repo.Save(User)
    
    ```
- **Роль в архитектуре**: Доменный/прикладной слой — чистая бизнес-логика, независимая от БД.

### <a id="bkmrk--8"></a>4. UseCase → Repository Interface

**Что происходит**: Абстрактный вызов сохранения.

- **Входные данные**: `User{Name: "Alice Johnson", Email: "alice@example.com"}`
- **Логирование**:
    
    ```
    [Repository-интерфейс] Вызов Save для User: {Name: "Alice Johnson", Email: "alice@example.com"}
    [Repository-интерфейс] Перенаправляю в реализацию (PostgresUserRepository)
    
    ```
- **Роль в архитектуре**: Доменный слой — интерфейс для инверсии зависимостей.

### <a id="bkmrk--9"></a>5. Repository Interface → Driver (PostgreSQL)

**Что происходит**: Формирование и выполнение SQL-запроса.

- **Реальные значения**:
    
    ```
    INSERT INTO users (name, email) 
    VALUES ('Alice Johnson', 'alice@example.com') 
    RETURNING id
    
    ```
    
    Результат: `ID=42`
- **Логирование**:
    
    ```
    [Драйвер] Получен вызов Save: User {Name: "Alice Johnson", Email: "alice@example.com"}
    [Драйвер] Формирую SQL: INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id
    [Драйвер] Выполняю запрос с параметрами: $1="Alice Johnson", $2="alice@example.com"
    [Драйвер] Результат: Новый ID=42, ошибок нет
    
    ```
- **Роль в архитектуре**: Инфраструктурный слой — детали реализации.

### <a id="bkmrk--10"></a>6. Database → Успешный ответ

**Что происходит**: Сохранение в PostgreSQL.

- **Реальные значения**: Вставка записи с `id=42`
- **Логирование**:
    
    ```
    [БД] Выполнена INSERT: Добавлена запись с id=42, name="Alice Johnson", email="alice@example.com"
    
    ```
- **Роль в архитектуре**: Внешняя зависимость в инфраструктуре.

---

## <a id="bkmrk--12"></a>Визуализация полного цикла на диаграмме

### <a id="bkmrk--13"></a>📊 Прямые стрелки (сплошные): Входящий поток

**Движение данных ОТ клиента К базе данных**

- `→` Обычные стрелки показывают передачу запроса и данных вниз по архитектуре
- На каждой стрелке подписано, какие именно данные передаются

### <a id="bkmrk--14"></a>📊 Обратные стрелки (пунктирные): Исходящий поток

**Движение результата ОТ базы данных К клиенту**

- `-.->` Пунктирные стрелки показывают возврат результата вверх по архитектуре
- На каждой обратной стрелке подписано, какой результат возвращается

---

<a id="bkmrk--19" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Roboto, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; font-size: 2.8275em; font-weight: 400; background-color: rgb(255, 255, 255);"></a><span style="color: rgb(34, 34, 34); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Roboto, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; font-size: 2.8275em; font-weight: 400;">Ключевые преимущества архитектуры</span>

1. **Изоляция слоев**: Смена БД не затрагивает бизнес-логику
2. **Тестируемость**: UseCase можно тестировать с mock-репозиториями
3. **Понятность**: Каждый слой имеет четкую ответственность
4. **Гибкость**: Легко добавлять новые адаптеры (GraphQL, gRPC)

## <a id="bkmrk--20"></a>Заключение

Этот пример показывает, как Clean Architecture делает поток данных предсказуемым и устойчивым. Каждый слой выполняет свою роль, а зависимости направлены правильно.