<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Selman Tunc</title>
	<atom:link href="https://selmantunc.com.tr/feed/" rel="self" type="application/rss+xml" />
	<link>https://selmantunc.com.tr</link>
	<description></description>
	<lastBuildDate>Mon, 08 Jun 2026 09:12:00 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://selmantunc.com.tr/wp-content/uploads/2023/07/cropped-tumblr_inline_oglumuMbgO1tyldvk_540-150x150-1-32x32.jpg</url>
	<title>Selman Tunc</title>
	<link>https://selmantunc.com.tr</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>ai nasil dusunur 1</title>
		<link>https://selmantunc.com.tr/ai/ai-nasil-dusunur-1/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Mon, 08 Jun 2026 09:10:23 +0000</pubDate>
				<category><![CDATA[ai]]></category>
		<guid isPermaLink="false">https://selmantunc.com.tr/?p=3554</guid>

					<description><![CDATA[Harika bir soru! Yazılım geliştirme süreçlerinde (özellikle Hexagonal Architecture veya Clean Architecture gibi katmanlı mimarilerde) yapılan bir değişikliğin uygulamanın neresinde &#8220;kırılmaya&#8221; veya &#8220;eksikliğe&#8221; yol açtığını bulmak tamamen veri akışını (data&#8230;]]></description>
										<content:encoded><![CDATA[<p>Harika bir soru! Yazılım geliştirme süreçlerinde (özellikle Hexagonal Architecture veya Clean Architecture gibi katmanlı mimarilerde) yapılan bir değişikliğin uygulamanın neresinde &#8220;kırılmaya&#8221; veya &#8220;eksikliğe&#8221; yol açtığını bulmak tamamen veri akışını (data flow) takip etmekten geçiyor.</p>
<p>Bu eksikliği fark etme sürecimi adım adım şöyle özetleyebilirim:</p>
<p>**1. &#8220;Burada Hatalar Var&#8221; Sözü Üzerine İlk Hipotezler**<br />
Siz bana `&#8221;burada hatalar var, tekrar düzenle&#8221;` dediğinizde ilk iş olarak arka planda `go build ./&#8230;` ve `go vet ./&#8230;` komutlarını çalıştırdım. Kod hiçbir derleme veya sözdizimi (syntax) hatası vermedi. Derleme hatası olmadığına göre sorunun **mantıksal (logical)** veya **eksik bir entegrasyon** olduğunu anladım. </p>
<p>**2. Veri Akışını (Data Flow) Zihnimde Canlandırma**<br />
Sisteminize yeni alanlar (`Word`, `RootAndStructure` vb.) eklemiştik. Bir verinin dışarıdan (API veya form) gelip veritabanına yazılma yolculuğunu düşündüm:<br />
`HTTP Handler -> App Service -> Domain Model -> Outbound Adapters (Postgres, Mongo vb.)`</p>
<p>Domain modeline ve Postgres&#8217;e bu alanları eklediğimi biliyordum. Ancak verinin bu iki nokta arasında kaybolabileceği yerleri aramaya başladım.</p>
<p>**3. Uygulama (Service) Katmanındaki Gözden Kaçan Detayı Bulma**<br />
Akışı takip ederek ilk olarak `app/service.go` dosyasına baktım. Gördüm ki `CreateDi` ve `UpdateDi` metotlarında, dışarıdan gelen `input` objesindeki veriler `Di` modeline manuel olarak kopyalanıyordu (`current.Explanation = input.Explanation` gibi). Ancak benim eklediğim yeni alanların hiçbiri burada atanmıyordu! Yani siz formu doldurup göndersiniz bile, Service katmanı bu yeni alanları umursamayıp çöpe atıyordu. </p>
<p>**4. &#8220;Service Katmanını Unuttuysam, Başka Neyi Unutmuş Olabilirim?&#8221; Sorusu**<br />
Service katmanındaki bu eşleştirmeyi atladığımı fark edince, kendi kendime *&#8221;Domain&#8217;i ve Postgres&#8217;i güncelledim ama demek ki bu modülün dışarıya bağlandığı diğer portları kontrol etmedim&#8221;* dedim. </p>
<p>Hemen `adapters/outbound` dizininin içine baktım. Orada sadece `postgres` olmadığını, bir de `mongodb` klasörü bulunduğunu gördüm. </p>
<p>**5. MongoDB&#8217;nin Yapısal Farklılığını Hatırlama ve Kontrol**<br />
Golang&#8217;de Postgres için `gorm` etiketleri kullanılır ve biz bunu `postgres/repository.go` içinde halletmiştik. Ancak MongoDB tamamen farklı bir NoSQL veritabanıdır ve verileri BSON formatında dökümanlar (documents) olarak tutar. Bu yüzden MongoDB&#8217;nin kendi struct&#8217;ları (genelde `bson` etiketlerine sahip) ve kendi Mapping (eşleştirme) fonksiyonları (`toDocument`, `toDomain`, `Upsert` işlemleri içindeki `bson.M{}`) olmak zorundaydı.</p>
<p>Hemen `adapters/outbound/mongodb/repository.go` dosyasını açıp okudum ve teorimin doğru olduğunu gördüm: Oradaki `DiDocument` struct&#8217;ında ve MongoDB&#8217;ye kayıt atan `Upsert` sorgusunda yeni alanların hiçbiri yoktu. </p>
<p>**Özetle:**<br />
Sizin &#8220;hata var&#8221; uyarınız derleme hatası üretmeyince, dedektif gibi verinin yaşam döngüsünü (Handler -> Service -> Database) adım adım izledim. Service katmanındaki kopukluğu bulunca, mimarinin diğer uç noktalarına da (MongoDB gibi) şüpheyle yaklaştım ve hatanın kök nedenini bu sistematik takip sayesinde fark ettim.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>git &#8211; Local’deki tüm tag’leri silmek için:</title>
		<link>https://selmantunc.com.tr/git/git-localdeki-tum-tagleri-silmek-icin/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Wed, 27 May 2026 21:50:52 +0000</pubDate>
				<category><![CDATA[git]]></category>
		<guid isPermaLink="false">https://selmantunc.com.tr/?p=3551</guid>

					<description><![CDATA[Local’deki tüm tag’leri silmek için: &#8220;`bash git tag -d $(git tag) &#8220;` Bu komut, yerel repodaki bütün tag’leri tek seferde siler. [git-scm](https://git-scm.com/docs/git-commit) İstersen önce hangi tag’ler olduğunu görmek için: &#8220;`bash&#8230;]]></description>
										<content:encoded><![CDATA[<p>Local’deki tüm tag’leri silmek için:</p>
<p>&#8220;`bash<br />
git tag -d $(git tag)<br />
&#8220;`</p>
<p>Bu komut, yerel repodaki bütün tag’leri tek seferde siler. [git-scm](https://git-scm.com/docs/git-commit)<br />
İstersen önce hangi tag’ler olduğunu görmek için:</p>
<p>&#8220;`bash<br />
git tag<br />
&#8220;`</p>
<p>Bir remote’daki tag’leri silmek farklı bir işlemdir; bu komut sadece **local** tag’leri kaldırır. [git-scm](https://git-scm.com/docs/git-commit)</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>git commit geri al</title>
		<link>https://selmantunc.com.tr/git/git-commit-geri-al/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Wed, 27 May 2026 21:50:38 +0000</pubDate>
				<category><![CDATA[git]]></category>
		<guid isPermaLink="false">https://selmantunc.com.tr/?p=3549</guid>

					<description><![CDATA[Son commit’i geri almak için birkaç seçenek var: &#8211; Commit’i geri al, değişiklikleri stage’de tut: &#8220;`bash git reset &#8211;soft HEAD~1 &#8220;` &#8211; Commit’i geri al, değişiklikleri working tree’de tut: &#8220;`bash&#8230;]]></description>
										<content:encoded><![CDATA[<p>Son commit’i geri almak için birkaç seçenek var:</p>
<p>&#8211; Commit’i geri al, değişiklikleri stage’de tut:<br />
&#8220;`bash<br />
git reset &#8211;soft HEAD~1<br />
&#8220;`</p>
<p>&#8211; Commit’i geri al, değişiklikleri working tree’de tut:<br />
&#8220;`bash<br />
git reset &#8211;mixed HEAD~1<br />
&#8220;`</p>
<p>&#8211; Commit’i tamamen sil, değişiklikleri de at:<br />
&#8220;`bash<br />
git reset &#8211;hard HEAD~1<br />
&#8220;`</p>
<p>Eğer commit’i remote’a push ettiysen, `reset &#8211;hard` yerine çoğu zaman şu daha güvenlidir:<br />
&#8220;`bash<br />
git revert HEAD<br />
&#8220;`</p>
<p>`git commit &#8211;amend` ise son commit’i silmez; son commit’i düzeltmek için kullanılır. [atlassian](https://www.atlassian.com/git/tutorials/saving-changes/git-commit)</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>git tag push</title>
		<link>https://selmantunc.com.tr/git/git-tag-push/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Wed, 27 May 2026 21:50:03 +0000</pubDate>
				<category><![CDATA[git]]></category>
		<guid isPermaLink="false">https://selmantunc.com.tr/?p=3547</guid>

					<description><![CDATA[Tüm yerel tag’leri remote’a göndermek için: &#8220;`bash git push &#8211;tags &#8220;` Belirli bir remote’a göndermek istersen: &#8220;`bash git push origin &#8211;tags &#8220;` Sadece tek bir tag göndermek için: &#8220;`bash git&#8230;]]></description>
										<content:encoded><![CDATA[<p>Tüm yerel tag’leri remote’a göndermek için:</p>
<p>&#8220;`bash<br />
git push &#8211;tags<br />
&#8220;`</p>
<p>Belirli bir remote’a göndermek istersen:</p>
<p>&#8220;`bash<br />
git push origin &#8211;tags<br />
&#8220;`</p>
<p>Sadece tek bir tag göndermek için:</p>
<p>&#8220;`bash<br />
git push origin <tag-adi><br />
&#8220;`</p>
<p>`&#8211;tags` tüm local tag’leri yollar; `&#8211;follow-tags` ise yalnızca commit’lerle ilişkili annotated tag’leri gönderir. [git-scm](https://git-scm.com/book/en/v2/Git-Basics-Tagging)</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>The Domain Concept for Hexagonal Architecture in Go (Golang)</title>
		<link>https://selmantunc.com.tr/golang/the-domain-concept-for-hexagonal-architecture-in-go-golang/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Mon, 13 Apr 2026 04:19:43 +0000</pubDate>
				<category><![CDATA[english]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[software]]></category>
		<guid isPermaLink="false">https://selmantunc.com.tr/?p=3541</guid>

					<description><![CDATA[Here is the **English version** of the Go (Golang) Hexagonal Architecture example, with the same structure and explanations. &#8212; # 📁 Folder Structure &#8220;`text hexagonal-go/ ├── go.mod ├── cmd/ │&#8230;]]></description>
										<content:encoded><![CDATA[<p>Here is the **English version** of the Go (Golang) Hexagonal Architecture example, with the same structure and explanations.</p>
<p>&#8212;</p>
<p># 📁 Folder Structure</p>
<p>&#8220;`text<br />
hexagonal-go/<br />
├── go.mod<br />
├── cmd/<br />
│   └── api/<br />
│       └── main.go<br />
└── internal/<br />
    ├── domain/<br />
    │   └── order/<br />
    │       ├── money.go<br />
    │       ├── order.go<br />
    │       ├── order_id.go<br />
    │       ├── order_status.go<br />
    │       └── repository.go<br />
    ├── application/<br />
    │   └── order_service.go<br />
    └── adapters/<br />
        ├── http/<br />
        │   └── order_handler.go<br />
        └── persistence/<br />
            └── memory/<br />
                └── order_repository.go<br />
&#8220;`</p>
<p>&#8212;</p>
<p># 1) `go.mod`</p>
<p>&#8220;`go<br />
module example.com/hexagonal-go</p>
<p>go 1.22<br />
&#8220;`</p>
<p>&#8212;</p>
<p># 2) Domain Layer</p>
<p>### `internal/domain/order/order_id.go`</p>
<p>&#8220;`go<br />
package order</p>
<p>type OrderID string<br />
&#8220;`</p>
<p>### `internal/domain/order/order_status.go`</p>
<p>&#8220;`go<br />
package order</p>
<p>type OrderStatus string</p>
<p>const (<br />
	OrderStatusCreated   OrderStatus = &#8220;CREATED&#8221;<br />
	OrderStatusCompleted OrderStatus = &#8220;COMPLETED&#8221;<br />
)<br />
&#8220;`</p>
<p>### `internal/domain/order/money.go`</p>
<p>&#8220;`go<br />
package order</p>
<p>import &#8220;fmt&#8221;</p>
<p>type Money struct {<br />
	Cents int64<br />
}</p>
<p>func NewMoney(cents int64) (Money, error) {<br />
	if cents < 0 {
		return Money{}, fmt.Errorf("money cannot be negative")
	}
	return Money{Cents: cents}, nil
}

func (m Money) Add(other Money) Money {
	return Money{Cents: m.Cents + other.Cents}
}
```

### `internal/domain/order/order.go`

```go
package order

import "fmt"

type OrderItem struct {
	ProductID string
	Quantity  int
	UnitPrice Money
}

type Order struct {
	id     OrderID
	items  []OrderItem
	status OrderStatus
}

func NewOrder(id OrderID) *Order {
	return &#038;Order{
		id:     id,
		items:  make([]OrderItem, 0),
		status: OrderStatusCreated,
	}
}

func (o *Order) ID() OrderID {
	return o.id
}

func (o *Order) Status() OrderStatus {
	return o.status
}

func (o *Order) Items() []OrderItem {
	copied := make([]OrderItem, len(o.items))
	copy(copied, o.items)
	return copied
}

func (o *Order) AddItem(productID string, quantity int, unitPrice Money) error {
	if o.status != OrderStatusCreated {
		return fmt.Errorf("order cannot be modified after completion")
	}
	if productID == "" {
		return fmt.Errorf("product id cannot be empty")
	}
	if quantity <= 0 {
		return fmt.Errorf("quantity must be greater than zero")
	}

	o.items = append(o.items, OrderItem{
		ProductID: productID,
		Quantity:  quantity,
		UnitPrice: unitPrice,
	})

	return nil
}

func (o *Order) Complete() error {
	if len(o.items) == 0 {
		return fmt.Errorf("order must have at least one item")
	}
	o.status = OrderStatusCompleted
	return nil
}

func (o *Order) Total() Money {
	total := Money{Cents: 0}
	for _, item := range o.items {
		total = total.Add(Money{Cents: item.UnitPrice.Cents * int64(item.Quantity)})
	}
	return total
}
```

### `internal/domain/order/repository.go`

```go
package order

import "context"

type Repository interface {
	Save(ctx context.Context, order *Order) error
	FindByID(ctx context.Context, id OrderID) (*Order, error)
}
```

---

# 3) Application Layer

### `internal/application/order_service.go`

```go
package application

import (
	"context"
	"fmt"
	"time"

	"example.com/hexagonal-go/internal/domain/order"
)

type OrderService struct {
	repo order.Repository
}

func NewOrderService(repo order.Repository) *OrderService {
	return &#038;OrderService{repo: repo}
}

func (s *OrderService) CreateOrder(ctx context.Context) (*order.Order, error) {
	id := order.OrderID(fmt.Sprintf("ord_%d", time.Now().UnixNano()))
	newOrder := order.NewOrder(id)

	if err := s.repo.Save(ctx, newOrder); err != nil {
		return nil, err
	}

	return newOrder, nil
}

func (s *OrderService) AddItem(ctx context.Context, orderID order.OrderID, productID string, quantity int, priceCents int64) (*order.Order, error) {
	o, err := s.repo.FindByID(ctx, orderID)
	if err != nil {
		return nil, err
	}

	price, err := order.NewMoney(priceCents)
	if err != nil {
		return nil, err
	}

	if err := o.AddItem(productID, quantity, price); err != nil {
		return nil, err
	}

	if err := s.repo.Save(ctx, o); err != nil {
		return nil, err
	}

	return o, nil
}

func (s *OrderService) CompleteOrder(ctx context.Context, orderID order.OrderID) (*order.Order, error) {
	o, err := s.repo.FindByID(ctx, orderID)
	if err != nil {
		return nil, err
	}

	if err := o.Complete(); err != nil {
		return nil, err
	}

	if err := s.repo.Save(ctx, o); err != nil {
		return nil, err
	}

	return o, nil
}

func (s *OrderService) GetOrder(ctx context.Context, orderID order.OrderID) (*order.Order, error) {
	return s.repo.FindByID(ctx, orderID)
}
```

---

# 4) Persistence Adapter (In-Memory)

### `internal/adapters/persistence/memory/order_repository.go`

```go
package memory

import (
	"context"
	"fmt"
	"sync"

	"example.com/hexagonal-go/internal/domain/order"
)

type OrderRepository struct {
	mu     sync.RWMutex
	orders map[order.OrderID]*order.Order
}

func NewOrderRepository() *OrderRepository {
	return &#038;OrderRepository{
		orders: make(map[order.OrderID]*order.Order),
	}
}

func (r *OrderRepository) Save(ctx context.Context, o *order.Order) error {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.orders[o.ID()] = o
	return nil
}

func (r *OrderRepository) FindByID(ctx context.Context, id order.OrderID) (*order.Order, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()

	o, ok := r.orders[id]
	if !ok {
		return nil, fmt.Errorf("order not found: %s", id)
	}

	return o, nil
}
```

---

# 5) HTTP Adapter

### `internal/adapters/http/order_handler.go`

```go
package httpadapter

import (
	"encoding/json"
	"net/http"
	"strings"

	"example.com/hexagonal-go/internal/application"
	"example.com/hexagonal-go/internal/domain/order"
)

type OrderHandler struct {
	service *application.OrderService
}

func NewOrderHandler(service *application.OrderService) *OrderHandler {
	return &#038;OrderHandler{service: service}
}

func (h *OrderHandler) RegisterRoutes(mux *http.ServeMux) {
	mux.HandleFunc("POST /orders", h.createOrder)
	mux.HandleFunc("GET /orders/", h.getOrder)
	mux.HandleFunc("POST /orders/{id}/items", h.addItem)
	mux.HandleFunc("POST /orders/{id}/complete", h.completeOrder)
}

func (h *OrderHandler) createOrder(w http.ResponseWriter, r *http.Request) {
	o, err := h.service.CreateOrder(r.Context())
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	respondJSON(w, http.StatusCreated, map[string]any{
		"id":     o.ID(),
		"status": o.Status(),
		"total":  o.Total().Cents,
	})
}

func (h *OrderHandler) getOrder(w http.ResponseWriter, r *http.Request) {
	id := extractID(r.URL.Path)
	o, err := h.service.GetOrder(r.Context(), order.OrderID(id))
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	respondJSON(w, http.StatusOK, map[string]any{
		"id":     o.ID(),
		"status": o.Status(),
		"items":  o.Items(),
		"total":  o.Total().Cents,
	})
}

func (h *OrderHandler) addItem(w http.ResponseWriter, r *http.Request) {
	id := extractID(r.URL.Path)

	var req struct {
		ProductID string `json:"product_id"`
		Quantity  int    `json:"quantity"`
		PriceCents int64  `json:"price_cents"`
	}

	if err := json.NewDecoder(r.Body).Decode(&#038;req); err != nil {
		http.Error(w, "invalid json", http.StatusBadRequest)
		return
	}

	o, err := h.service.AddItem(r.Context(), order.OrderID(id), req.ProductID, req.Quantity, req.PriceCents)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	respondJSON(w, http.StatusOK, map[string]any{
		"id":     o.ID(),
		"status": o.Status(),
		"total":  o.Total().Cents,
	})
}

func (h *OrderHandler) completeOrder(w http.ResponseWriter, r *http.Request) {
	id := extractID(r.URL.Path)
	o, err := h.service.CompleteOrder(r.Context(), order.OrderID(id))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	respondJSON(w, http.StatusOK, map[string]any{
		"id":     o.ID(),
		"status": o.Status(),
		"total":  o.Total().Cents,
	})
}

func extractID(path string) string {
	parts := strings.Split(strings.Trim(path, "/"), "/")
	if len(parts) >= 2 {<br />
		return parts[1]<br />
	}<br />
	return &#8220;&#8221;<br />
}</p>
<p>func respondJSON(w http.ResponseWriter, status int, payload any) {<br />
	w.Header().Set(&#8220;Content-Type&#8221;, &#8220;application/json&#8221;)<br />
	w.WriteHeader(status)<br />
	_ = json.NewEncoder(w).Encode(payload)<br />
}<br />
&#8220;`</p>
<p>&#8212;</p>
<p># 6) Application Entry Point</p>
<p>### `cmd/api/main.go`</p>
<p>&#8220;`go<br />
package main</p>
<p>import (<br />
	&#8220;log&#8221;<br />
	&#8220;net/http&#8221;</p>
<p>	httpadapter &#8220;example.com/hexagonal-go/internal/adapters/http&#8221;<br />
	&#8220;example.com/hexagonal-go/internal/adapters/persistence/memory&#8221;<br />
	&#8220;example.com/hexagonal-go/internal/application&#8221;<br />
)</p>
<p>func main() {<br />
	repo := memory.NewOrderRepository()<br />
	service := application.NewOrderService(repo)<br />
	handler := httpadapter.NewOrderHandler(service)</p>
<p>	mux := http.NewServeMux()<br />
	handler.RegisterRoutes(mux)</p>
<p>	log.Println(&#8220;server started on :8080&#8221;)<br />
	if err := http.ListenAndServe(&#8220;:8080&#8221;, mux); err != nil {<br />
		log.Fatal(err)<br />
	}<br />
}<br />
&#8220;`</p>
<p>&#8212;</p>
<p># 7) How It Works</p>
<p>* **HTTP adapter** receives the request<br />
* **Application service** executes the use case<br />
* **Domain** applies business rules<br />
* **Repository (port)** is defined in the domain<br />
* **Adapter (memory)** implements it</p>
<p>👉 The domain layer:</p>
<p>* does NOT know `net/http`<br />
* does NOT know databases<br />
* does NOT know frameworks</p>
<p>&#8212;</p>
<p># 8) Simple Test Example</p>
<p>### `internal/domain/order/order_test.go`</p>
<p>&#8220;`go<br />
package order</p>
<p>import &#8220;testing&#8221;</p>
<p>func TestOrderCompleteWithoutItems(t *testing.T) {<br />
	o := NewOrder(&#8220;1&#8221;)</p>
<p>	if err := o.Complete(); err == nil {<br />
		t.Fatal(&#8220;expected error when completing empty order&#8221;)<br />
	}<br />
}</p>
<p>func TestOrderTotal(t *testing.T) {<br />
	o := NewOrder(&#8220;1&#8221;)<br />
	price, _ := NewMoney(500)</p>
<p>	if err := o.AddItem(&#8220;p1&#8221;, 2, price); err != nil {<br />
		t.Fatal(err)<br />
	}</p>
<p>	if o.Total().Cents != 1000 {<br />
		t.Fatalf(&#8220;expected 1000, got %d&#8221;, o.Total().Cents)<br />
	}<br />
}<br />
&#8220;`</p>
<p>&#8212;</p>
<p># 9) Run the Project</p>
<p>&#8220;`bash<br />
go run ./cmd/api<br />
&#8220;`</p>
<p>Example requests:</p>
<p>&#8220;`bash<br />
curl -X POST localhost:8080/orders<br />
&#8220;`</p>
<p>&#8220;`bash<br />
curl -X POST localhost:8080/orders/ord_xxx/items \<br />
  -H &#8216;Content-Type: application/json&#8217; \<br />
  -d &#8216;{&#8220;product_id&#8221;:&#8221;p1&#8243;,&#8221;quantity&#8221;:2,&#8221;price_cents&#8221;:500}&#8217;<br />
&#8220;`</p>
<p>&#8220;`bash<br />
curl -X POST localhost:8080/orders/ord_xxx/complete<br />
&#8220;`</p>
<p>&#8212;</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Hexagonal Mimari için domain kavrami  Go (Golang)</title>
		<link>https://selmantunc.com.tr/software/hexagonal-mimari-icin-domain-kavrami-go-golang/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Mon, 13 Apr 2026 04:18:24 +0000</pubDate>
				<category><![CDATA[software]]></category>
		<guid isPermaLink="false">https://selmantunc.com.tr/?p=3538</guid>

					<description><![CDATA[## Klasör yapısı &#8220;`text hexagonal-go/ ├── go.mod ├── cmd/ │ └── api/ │ └── main.go └── internal/ ├── domain/ │ └── order/ │ ├── money.go │ ├── order.go │ ├──&#8230;]]></description>
										<content:encoded><![CDATA[<p>## Klasör yapısı</p>
<p>&#8220;`text<br />
hexagonal-go/<br />
├── go.mod<br />
├── cmd/<br />
│   └── api/<br />
│       └── main.go<br />
└── internal/<br />
    ├── domain/<br />
    │   └── order/<br />
    │       ├── money.go<br />
    │       ├── order.go<br />
    │       ├── order_id.go<br />
    │       ├── order_status.go<br />
    │       └── repository.go<br />
    ├── application/<br />
    │   └── order_service.go<br />
    └── adapters/<br />
        ├── http/<br />
        │   └── order_handler.go<br />
        └── persistence/<br />
            └── memory/<br />
                └── order_repository.go<br />
&#8220;`</p>
<p>&#8212;</p>
<p>## 1) `go.mod`</p>
<p>&#8220;`go<br />
module example.com/hexagonal-go</p>
<p>go 1.22<br />
&#8220;`</p>
<p>&#8212;</p>
<p>## 2) Domain katmanı</p>
<p>### `internal/domain/order/order_id.go`</p>
<p>&#8220;`go<br />
package order</p>
<p>type OrderID string<br />
&#8220;`</p>
<p>### `internal/domain/order/order_status.go`</p>
<p>&#8220;`go<br />
package order</p>
<p>type OrderStatus string</p>
<p>const (<br />
	OrderStatusCreated   OrderStatus = &#8220;CREATED&#8221;<br />
	OrderStatusCompleted OrderStatus = &#8220;COMPLETED&#8221;<br />
)<br />
&#8220;`</p>
<p>### `internal/domain/order/money.go`</p>
<p>&#8220;`go<br />
package order</p>
<p>import &#8220;fmt&#8221;</p>
<p>type Money struct {<br />
	Cents int64<br />
}</p>
<p>func NewMoney(cents int64) (Money, error) {<br />
	if cents < 0 {
		return Money{}, fmt.Errorf("money cannot be negative")
	}
	return Money{Cents: cents}, nil
}

func (m Money) Add(other Money) Money {
	return Money{Cents: m.Cents + other.Cents}
}
```

### `internal/domain/order/order.go`

```go
package order

import "fmt"

type OrderItem struct {
	ProductID string
	Quantity  int
	UnitPrice Money
}

type Order struct {
	id     OrderID
	items  []OrderItem
	status OrderStatus
}

func NewOrder(id OrderID) *Order {
	return &#038;Order{
		id:     id,
		items:  make([]OrderItem, 0),
		status: OrderStatusCreated,
	}
}

func (o *Order) ID() OrderID {
	return o.id
}

func (o *Order) Status() OrderStatus {
	return o.status
}

func (o *Order) Items() []OrderItem {
	copied := make([]OrderItem, len(o.items))
	copy(copied, o.items)
	return copied
}

func (o *Order) AddItem(productID string, quantity int, unitPrice Money) error {
	if o.status != OrderStatusCreated {
		return fmt.Errorf("order cannot be modified after completion")
	}
	if productID == "" {
		return fmt.Errorf("product id cannot be empty")
	}
	if quantity <= 0 {
		return fmt.Errorf("quantity must be greater than zero")
	}

	o.items = append(o.items, OrderItem{
		ProductID: productID,
		Quantity:  quantity,
		UnitPrice: unitPrice,
	})

	return nil
}

func (o *Order) Complete() error {
	if len(o.items) == 0 {
		return fmt.Errorf("order must have at least one item")
	}
	o.status = OrderStatusCompleted
	return nil
}

func (o *Order) Total() Money {
	total := Money{Cents: 0}
	for _, item := range o.items {
		total = total.Add(Money{Cents: item.UnitPrice.Cents * int64(item.Quantity)})
	}
	return total
}
```

### `internal/domain/order/repository.go`

```go
package order

import "context"

type Repository interface {
	Save(ctx context.Context, order *Order) error
	FindByID(ctx context.Context, id OrderID) (*Order, error)
}
```

---

## 3) Application katmanı

### `internal/application/order_service.go`

```go
package application

import (
	"context"
	"fmt"
	"time"

	"example.com/hexagonal-go/internal/domain/order"
)

type OrderService struct {
	repo order.Repository
}

func NewOrderService(repo order.Repository) *OrderService {
	return &#038;OrderService{repo: repo}
}

func (s *OrderService) CreateOrder(ctx context.Context) (*order.Order, error) {
	id := order.OrderID(fmt.Sprintf("ord_%d", time.Now().UnixNano()))
	newOrder := order.NewOrder(id)

	if err := s.repo.Save(ctx, newOrder); err != nil {
		return nil, err
	}

	return newOrder, nil
}

func (s *OrderService) AddItem(ctx context.Context, orderID order.OrderID, productID string, quantity int, priceCents int64) (*order.Order, error) {
	o, err := s.repo.FindByID(ctx, orderID)
	if err != nil {
		return nil, err
	}

	price, err := order.NewMoney(priceCents)
	if err != nil {
		return nil, err
	}

	if err := o.AddItem(productID, quantity, price); err != nil {
		return nil, err
	}

	if err := s.repo.Save(ctx, o); err != nil {
		return nil, err
	}

	return o, nil
}

func (s *OrderService) CompleteOrder(ctx context.Context, orderID order.OrderID) (*order.Order, error) {
	o, err := s.repo.FindByID(ctx, orderID)
	if err != nil {
		return nil, err
	}

	if err := o.Complete(); err != nil {
		return nil, err
	}

	if err := s.repo.Save(ctx, o); err != nil {
		return nil, err
	}

	return o, nil
}

func (s *OrderService) GetOrder(ctx context.Context, orderID order.OrderID) (*order.Order, error) {
	return s.repo.FindByID(ctx, orderID)
}
```

---

## 4) Persistence adapter (in-memory)

### `internal/adapters/persistence/memory/order_repository.go`

```go
package memory

import (
	"context"
	"fmt"
	"sync"

	"example.com/hexagonal-go/internal/domain/order"
)

type OrderRepository struct {
	mu     sync.RWMutex
	orders map[order.OrderID]*order.Order
}

func NewOrderRepository() *OrderRepository {
	return &#038;OrderRepository{
		orders: make(map[order.OrderID]*order.Order),
	}
}

func (r *OrderRepository) Save(ctx context.Context, o *order.Order) error {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.orders[o.ID()] = o
	return nil
}

func (r *OrderRepository) FindByID(ctx context.Context, id order.OrderID) (*order.Order, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()

	o, ok := r.orders[id]
	if !ok {
		return nil, fmt.Errorf("order not found: %s", id)
	}

	return o, nil
}
```

---

## 5) HTTP adapter

### `internal/adapters/http/order_handler.go`

```go
package httpadapter

import (
	"encoding/json"
	"net/http"
	"strings"

	"example.com/hexagonal-go/internal/application"
	"example.com/hexagonal-go/internal/domain/order"
)

type OrderHandler struct {
	service *application.OrderService
}

func NewOrderHandler(service *application.OrderService) *OrderHandler {
	return &#038;OrderHandler{service: service}
}

func (h *OrderHandler) RegisterRoutes(mux *http.ServeMux) {
	mux.HandleFunc("POST /orders", h.createOrder)
	mux.HandleFunc("GET /orders/", h.getOrder)
	mux.HandleFunc("POST /orders/{id}/items", h.addItem)
	mux.HandleFunc("POST /orders/{id}/complete", h.completeOrder)
}

func (h *OrderHandler) createOrder(w http.ResponseWriter, r *http.Request) {
	o, err := h.service.CreateOrder(r.Context())
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	respondJSON(w, http.StatusCreated, map[string]any{
		"id":     o.ID(),
		"status": o.Status(),
		"total":  o.Total().Cents,
	})
}

func (h *OrderHandler) getOrder(w http.ResponseWriter, r *http.Request) {
	id := extractID(r.URL.Path)
	o, err := h.service.GetOrder(r.Context(), order.OrderID(id))
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	respondJSON(w, http.StatusOK, map[string]any{
		"id":     o.ID(),
		"status": o.Status(),
		"items":  o.Items(),
		"total":  o.Total().Cents,
	})
}

func (h *OrderHandler) addItem(w http.ResponseWriter, r *http.Request) {
	id := extractID(r.URL.Path)

	var req struct {
		ProductID string `json:"product_id"`
		Quantity  int    `json:"quantity"`
		PriceCents int64  `json:"price_cents"`
	}

	if err := json.NewDecoder(r.Body).Decode(&#038;req); err != nil {
		http.Error(w, "invalid json", http.StatusBadRequest)
		return
	}

	o, err := h.service.AddItem(r.Context(), order.OrderID(id), req.ProductID, req.Quantity, req.PriceCents)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	respondJSON(w, http.StatusOK, map[string]any{
		"id":     o.ID(),
		"status": o.Status(),
		"total":  o.Total().Cents,
	})
}

func (h *OrderHandler) completeOrder(w http.ResponseWriter, r *http.Request) {
	id := extractID(r.URL.Path)
	o, err := h.service.CompleteOrder(r.Context(), order.OrderID(id))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	respondJSON(w, http.StatusOK, map[string]any{
		"id":     o.ID(),
		"status": o.Status(),
		"total":  o.Total().Cents,
	})
}

func extractID(path string) string {
	parts := strings.Split(strings.Trim(path, "/"), "/")
	if len(parts) >= 2 {<br />
		return parts[1]<br />
	}<br />
	return &#8220;&#8221;<br />
}</p>
<p>func respondJSON(w http.ResponseWriter, status int, payload any) {<br />
	w.Header().Set(&#8220;Content-Type&#8221;, &#8220;application/json&#8221;)<br />
	w.WriteHeader(status)<br />
	_ = json.NewEncoder(w).Encode(payload)<br />
}<br />
&#8220;`</p>
<p>&#8212;</p>
<p>## 6) Uygulama başlangıcı</p>
<p>### `cmd/api/main.go`</p>
<p>&#8220;`go<br />
package main</p>
<p>import (<br />
	&#8220;log&#8221;<br />
	&#8220;net/http&#8221;</p>
<p>	httpadapter &#8220;example.com/hexagonal-go/internal/adapters/http&#8221;<br />
	&#8220;example.com/hexagonal-go/internal/adapters/persistence/memory&#8221;<br />
	&#8220;example.com/hexagonal-go/internal/application&#8221;<br />
)</p>
<p>func main() {<br />
	repo := memory.NewOrderRepository()<br />
	service := application.NewOrderService(repo)<br />
	handler := httpadapter.NewOrderHandler(service)</p>
<p>	mux := http.NewServeMux()<br />
	handler.RegisterRoutes(mux)</p>
<p>	log.Println(&#8220;server started on :8080&#8221;)<br />
	if err := http.ListenAndServe(&#8220;:8080&#8221;, mux); err != nil {<br />
		log.Fatal(err)<br />
	}<br />
}<br />
&#8220;`</p>
<p>&#8212;</p>
<p>## 7) Akış nasıl çalışıyor?</p>
<p>* **HTTP adapter** isteği alır.<br />
* **Application service** use-case’i yürütür.<br />
* **Domain** iş kurallarını uygular.<br />
* **Repository port** domain tarafından tanımlanır.<br />
* **Memory adapter** bu portu implement eder.</p>
<p>Bu yapı sayesinde domain katmanı:</p>
<p>* `net/http` bilmez,<br />
* database bilmez,<br />
* framework bilmez.</p>
<p>&#8212;</p>
<p>## 8) Kısa test örneği</p>
<p>### `internal/domain/order/order_test.go`</p>
<p>&#8220;`go<br />
package order</p>
<p>import &#8220;testing&#8221;</p>
<p>func TestOrderCompleteWithoutItems(t *testing.T) {<br />
	o := NewOrder(&#8220;1&#8221;)</p>
<p>	if err := o.Complete(); err == nil {<br />
		t.Fatal(&#8220;expected error when completing empty order&#8221;)<br />
	}<br />
}</p>
<p>func TestOrderTotal(t *testing.T) {<br />
	o := NewOrder(&#8220;1&#8221;)<br />
	price, _ := NewMoney(500)</p>
<p>	if err := o.AddItem(&#8220;p1&#8221;, 2, price); err != nil {<br />
		t.Fatal(err)<br />
	}</p>
<p>	if o.Total().Cents != 1000 {<br />
		t.Fatalf(&#8220;expected 1000, got %d&#8221;, o.Total().Cents)<br />
	}<br />
}<br />
&#8220;`</p>
<p>&#8212;</p>
<p>## 9) Çalıştırma</p>
<p>&#8220;`bash<br />
go run ./cmd/api<br />
&#8220;`</p>
<p>Örnek istek:</p>
<p>&#8220;`bash<br />
curl -X POST localhost:8080/orders<br />
&#8220;`</p>
<p>&#8220;`bash<br />
curl -X POST localhost:8080/orders/ord_xxx/items \<br />
  -H &#8216;Content-Type: application/json&#8217; \<br />
  -d &#8216;{&#8220;product_id&#8221;:&#8221;p1&#8243;,&#8221;quantity&#8221;:2,&#8221;price_cents&#8221;:500}&#8217;<br />
&#8220;`</p>
<p>&#8220;`bash<br />
curl -X POST localhost:8080/orders/ord_xxx/complete<br />
&#8220;`</p>
<p>&#8212;</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>c *fiber.Ctx bunu nasil bir fonkyon icinde init ederim</title>
		<link>https://selmantunc.com.tr/golang/c-fiber-ctx-bunu-nasil-bir-fonkyon-icinde-init-ederim/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Sun, 12 Apr 2026 03:19:49 +0000</pubDate>
				<category><![CDATA[golang]]></category>
		<guid isPermaLink="false">https://selmantunc.com.tr/?p=3536</guid>

					<description><![CDATA[Fiber&#8217;deki `*fiber.Ctx` nesnesi, bir HTTP isteği sırasında oluşan **istek (request)** ve **yanıt (response)** döngüsünü yönetir. Bu nesne sadece bir handler (yönlendirici) fonksiyonu içinde **Fiber tarafından oluşturulur**. Yani `*fiber.Ctx` nesnesini dışarıdan&#8230;]]></description>
										<content:encoded><![CDATA[<p>Fiber&#8217;deki `*fiber.Ctx` nesnesi, bir HTTP isteği sırasında oluşan **istek (request)** ve **yanıt (response)** döngüsünü yönetir. Bu nesne sadece bir handler (yönlendirici) fonksiyonu içinde **Fiber tarafından oluşturulur**.</p>
<p>Yani `*fiber.Ctx` nesnesini dışarıdan manuel olarak &#8220;başlatamazsınız&#8221; (instantiate edemezsiniz). Ancak, bu nesneyi ihtiyaç duyduğunuz yardımcı fonksiyonlara **parametre olarak** geçirebilirsiniz.</p>
<p>İşte temiz ve modüler bir mimari için uygulayabileceğiniz yöntemler:</p>
<p>### 1. Yöntem: Handler İçinde Hazırlık ve Parametre Olarak Geçirme (En İyi Pratik)</p>
<p>Handler fonksiyonu Fiber ile konuşur, ancak asıl işi yapan &#8220;Service&#8221; katmanındaki fonksiyonlara `*fiber.Ctx` yerine sadece ihtiyacı olan verileri (örneğin sadece `limit` veya `search` değerini) geçirmek en iyi yaklaşımdır.</p>
<p>&#8220;`go<br />
// Service Katmanı (Logic fonksiyonu)<br />
func GetPostsFromDB(db *gorm.DB, start int, length int, search string) ([]Post, int64, error) {<br />
    // Sadece veriyle ilgilenir, Fiber&#8217;den bağımsızdır.<br />
    var posts []Post<br />
    var count int64</p>
<p>    // İşlemler&#8230;<br />
    return posts, count, nil<br />
}</p>
<p>// Handler Katmanı<br />
app.Get(&#8220;/api/posts&#8221;, func(c *fiber.Ctx) error {<br />
    // Fiber&#8217;den verileri al<br />
    start := c.QueryInt(&#8220;start&#8221;, 0)</p>
<p>    // Servis fonksiyonunu çağır<br />
    posts, count, _ := GetPostsFromDB(db, start, 10, &#8220;&#8221;)</p>
<p>    return c.JSON(fiber.Map{&#8220;data&#8221;: posts, &#8220;total&#8221;: count})<br />
})<br />
&#8220;`</p>
<p>### 2. Yöntem: Context&#8217;i Fonksiyona Taşımak</p>
<p>Eğer fonksiyonun içinde `c.Status()`, `c.JSON()` veya `c.Params()` gibi Fiber özelliklerine sürekli ihtiyacınız varsa, fonksiyon imzasını şu şekilde kurabilirsiniz:</p>
<p>&#8220;`go<br />
func HandleRequestData(c *fiber.Ctx) error {<br />
    // Bu fonksiyon doğrudan Fiber context&#8217;ini kullanabilir<br />
    search := c.Query(&#8220;search&#8221;)</p>
<p>    // &#8230; işlemler<br />
    return c.JSON(fiber.Map{&#8220;search&#8221;: search})<br />
}</p>
<p>// Kullanımı<br />
app.Get(&#8220;/search&#8221;, HandleRequestData)<br />
&#8220;`</p>
<p>### 3. Yöntem: `Locals` Kullanımı (Veri Paylaşımı)</p>
<p>`*fiber.Ctx` içerisindeki `.Locals()` fonksiyonunu kullanarak, middleware&#8217;de başlattığınız bir veriyi (örneğin Auth bilgisi veya User ID) tüm fonksiyonlarınızda kullanabilirsiniz.</p>
<p>&#8220;`go<br />
// Middleware<br />
app.Use(func(c *fiber.Ctx) error {<br />
    c.Locals(&#8220;userID&#8221;, 123) // Bir kez set et<br />
    return c.Next()<br />
})</p>
<p>// İstediğiniz herhangi bir fonksiyonda<br />
func MyHandler(c *fiber.Ctx) error {<br />
    userID := c.Locals(&#8220;userID&#8221;) // Buradan al<br />
    return c.SendString(fmt.Sprintf(&#8220;User: %v&#8221;, userID))<br />
}<br />
&#8220;`</p>
<p>### Neden `*fiber.Ctx` ile &#8220;Init&#8221; yapmamalısınız?</p>
<p>* **Test Edilebilirlik:** Eğer fonksiyonlarınızı `*fiber.Ctx`&#8217;e bağımlı yaparsanız, o fonksiyonu bir HTTP isteği olmadan test edemezsiniz (Unit test yazamazsınız).<br />
* **Bağımsızlık:** Fonksiyonlarınızı `c.Query()`, `c.Params()` yerine doğrudan `int`, `string` gibi standart Go tiplerini kabul edecek şekilde tasarlarsanız, aynı fonksiyonu hem HTTP API&#8217;nizde hem de bir CLI komutunda veya bir background job&#8217;da değiştirmeden kullanabilirsiniz.</p>
<p>**Özetle:** `*fiber.Ctx` nesnesini başlatmaya çalışmayın. `*fiber.Ctx`&#8217;ten verileri çıkarın (`c.QueryInt` gibi) ve bu saf verileri kendi yazdığınız fonksiyonlara parametre olarak gönderin. </p>
<p>Bu şekilde kodunuzu &#8220;Fiber&#8217;e bağlı&#8221; olmaktan kurtarıp &#8220;saf Go kodu&#8221; haline getirmiş olursunuz. Bu yaklaşım kod tabanınızı uzun vadede çok daha sürdürülebilir kılacaktır. Başka bir katman mimarisi (Service/Repository pattern) üzerinde çalışıyor musunuz?</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>php development-environment-comparison</title>
		<link>https://selmantunc.com.tr/php/php-development-environment-comparison/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Wed, 18 Mar 2026 08:05:06 +0000</pubDate>
				<category><![CDATA[php]]></category>
		<guid isPermaLink="false">https://selmantunc.com.tr/?p=3531</guid>

					<description><![CDATA[https://www.servbay.com/tr/pricing ServBay&#8217;in ücretsiz sürümü, ücretli Laravel Herd Pro&#8217;yu tam anlamıyla karşılayabilir. ServBay&#8217;de özel alan adları destekleniyor mu? Elbette! ServBay, özel alan &#8230;Read more I made an App like Laravel Herd.&#8230;]]></description>
										<content:encoded><![CDATA[<p>https://www.servbay.com/tr/pricing</p>
<p>ServBay&#8217;in ücretsiz sürümü, ücretli Laravel Herd Pro&#8217;yu tam anlamıyla karşılayabilir. ServBay&#8217;de özel alan adları destekleniyor mu? Elbette! ServBay, özel alan &#8230;Read more</p>
<blockquote class="reddit-embed-bq" style="height:316px" ><p><a href="https://www.reddit.com/r/macapps/comments/1bctvoy/i_made_an_app_like_laravel_herd_supports_caddy/">I made an App like Laravel Herd. supports caddy, nginx, apache, php, nodejs, mysql, mariadb, postgresql, mongodb, redis, memcached&#8230;</a><br /> by<a href="https://www.reddit.com/user/PhpWebStudy/">u/PhpWebStudy</a> in<a href="https://www.reddit.com/r/macapps/">macapps</a></p></blockquote>
<p><script async src="https://embed.reddit.com/widgets.js" charset="UTF-8"></script></p>
<p>https://dudi.dev/laravel-development-environment-comparison</p>
<blockquote class="wp-embedded-content" data-secret="YOKpcu9dnp"><p><a href="https://selmantunc.com.tr/uncategorized/macos-valet-install/">macos valet install</a></p></blockquote>
<p><iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted"  title="&#8220;macos valet install&#8221; &#8212; Selman Tunc" src="https://selmantunc.com.tr/uncategorized/macos-valet-install/embed/#?secret=eNrwc1OYev#?secret=YOKpcu9dnp" data-secret="YOKpcu9dnp" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe></p>
<p>laravel valet </p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Chrome Security and Passive Mode Risks</title>
		<link>https://selmantunc.com.tr/software/chrome-security-and-passive-mode-risks/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Thu, 05 Mar 2026 07:06:24 +0000</pubDate>
				<category><![CDATA[software]]></category>
		<guid isPermaLink="false">https://selmantunc.com.tr/?p=3527</guid>

					<description><![CDATA[No, technically, a completely **disabled** (deactivated) Chrome extension cannot steal new information or monitor your current browsing activity. When an extension is disabled, it is not loaded into the browser&#8217;s&#8230;]]></description>
										<content:encoded><![CDATA[<p>No, technically, a completely **disabled** (deactivated) Chrome extension cannot steal new information or monitor your current browsing activity. When an extension is disabled, it is not loaded into the browser&#8217;s memory and its background processes are terminated. [support.google](https://support.google.com/chrome/thread/11119336/can-chrome-extensions-affect-computer-performance-even-if-they-are-turned-off?hl=en)</p>
<p>However, there are two critical security nuances to consider:</p>
<p>### Residual Risks in Open Tabs<br />
If a malicious extension was active and you disabled it while having several tabs open, its &#8220;content scripts&#8221; might still be running in those specific pages until they are refreshed. [stackoverflow](https://stackoverflow.com/questions/69368249/what-data-is-accessible-to-a-disabled-chrome-extension)<br />
&#8211; These scripts could potentially leak data through indirect methods like hidden images or CSS if the site&#8217;s Content Security Policy (CSP) is weak. [stackoverflow](https://stackoverflow.com/questions/69368249/what-data-is-accessible-to-a-disabled-chrome-extension)<br />
&#8211; **Solution:** You must refresh all open tabs or restart your browser after disabling an extension to ensure all its scripts are fully cleared. [stackoverflow](https://stackoverflow.com/questions/69368249/what-data-is-accessible-to-a-disabled-chrome-extension)</p>
<p>### Previously Stolen Data<br />
Disabling an extension only stops **future** data collection; it does not &#8220;undo&#8221; what was already stolen while the extension was active. [youtube](https://www.youtube.com/watch?v=Rhxz_i8rDcM)<br />
&#8211; If a malicious extension captured your passwords, session cookies, or personal data while it was enabled, that information is likely already stored on the attacker&#8217;s server (C2 server). [linkedin](https://www.linkedin.com/pulse/warning-malicious-chrome-extensions-steal-your-gtrae)<br />
&#8211; Attackers often use stolen session tokens to maintain access to your accounts even after the extension is deleted. [thehackernews](https://thehackernews.com/2026/02/malicious-chrome-extensions-caught.html)</p>
<p>### Extension Status Comparison<br />
The following table illustrates the capabilities of an extension based on its state:</p>
<p>| Status | Web Page Access | Real-time Data Leak | Historical Data Risk |<br />
| :&#8212; | :&#8212; | :&#8212; | :&#8212; |<br />
| **Enabled** | Full Access  [reddit](https://www.reddit.com/r/chrome/comments/1ckcd1s/can_chrome_extensions_scrape_my_pages_and_steal/) | High  [youtube](https://www.youtube.com/watch?v=Rhxz_i8rDcM) | Continuous Access |<br />
| **Disabled** | Blocked  [support.google](https://support.google.com/chrome/thread/11119336/can-chrome-extensions-affect-computer-performance-even-if-they-are-turned-off?hl=en) | Very Low (Residual) | Stored on External Server |<br />
| **Removed** | Impossible | Impossible | Stored on External Server |</p>
<p>If you suspect an extension is malicious, simply disabling it is not enough. You should **remove** it entirely, clear your browser cookies, and change your passwords for sensitive accounts. [support.google](https://support.google.com/chrome/thread/306226825/chrome-extensions-might-steal-passwords?hl=en)</p>
<p>Which specific extension are you concerned about?</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Google Chrome&#8217;da bir eklentiyi tamamen devre dışı (deactive) birakinca bilgi calabilir mi?</title>
		<link>https://selmantunc.com.tr/software/google-chromeda-bir-eklentiyi-tamamen-devre-disi-deactive-birakinca-bilgi-calabilir-mi/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Thu, 05 Mar 2026 07:05:31 +0000</pubDate>
				<category><![CDATA[software]]></category>
		<guid isPermaLink="false">https://selmantunc.com.tr/?p=3525</guid>

					<description><![CDATA[Hayır, Google Chrome&#8217;da bir eklentiyi tamamen devre dışı (deactive) bırakmak, o eklentinin yeni veri toplamasını veya çalmasını teknik olarak engeller. Eklenti pasif durumdayken tarayıcı belleğine yüklenmez ve web sayfalarındaki trafiğinize&#8230;]]></description>
										<content:encoded><![CDATA[<p>Hayır, Google Chrome&#8217;da bir eklentiyi tamamen devre dışı (deactive) bırakmak, o eklentinin yeni veri toplamasını veya çalmasını teknik olarak engeller. Eklenti pasif durumdayken tarayıcı belleğine yüklenmez ve web sayfalarındaki trafiğinize müdahale edemez. [support.google](https://support.google.com/chrome/thread/11119336/can-chrome-extensions-affect-computer-performance-even-if-they-are-turned-off?hl=en)</p>
<p>Ancak güvenlik açısından dikkat edilmesi gereken iki önemli istisna bulunmaktadır:</p>
<p>### Aktif Sekmelerdeki Kalıntı Riskler<br />
Bir eklentiyi devre dışı bıraktığınız an, o sırada açık olan sekmelerde halihazırda enjekte edilmiş olan &#8220;içerik betikleri&#8221; (content scripts) çalışmaya devam edebilir. [stackoverflow](https://stackoverflow.com/questions/69368249/what-data-is-accessible-to-a-disabled-chrome-extension)<br />
&#8211; Bu betikler eklenti merkezine veri gönderemez ancak sitenin kendi güvenlik politikası (CSP) zayıfsa, verileri gizli görseller veya CSS üzerinden dışarı sızdırmaya çalışabilir. [stackoverflow](https://stackoverflow.com/questions/69368249/what-data-is-accessible-to-a-disabled-chrome-extension)<br />
&#8211; **Çözüm:** Eklentiyi kapattıktan sonra açık olan tüm sekmeleri yenilemek (Refresh) bu riski tamamen ortadan kaldırır. [stackoverflow](https://stackoverflow.com/questions/69368249/what-data-is-accessible-to-a-disabled-chrome-extension)</p>
<p>### Geçmişte Toplanan Veriler<br />
Eklentinin devre dışı bırakılması sadece gelecekteki veri akışını keser.<br />
&#8211; Eğer eklenti aktif olduğu süre boyunca şifrelerinizi, çerezlerinizi (cookies) veya kişisel verilerinizi uzak bir sunucuya (C2 server) gönderdiyse, bu veriler saldırganın elinde kalmaya devam eder. [youtube](https://www.youtube.com/watch?v=Rhxz_i8rDcM)<br />
&#8211; Kötü niyetli eklentiler genellikle oturum jetonlarını (session tokens) çalarak, siz eklentiyi silseniz bile hesaplarınıza erişim sağlamaya devam edebilir. [linkedin](https://www.linkedin.com/pulse/warning-malicious-chrome-extensions-steal-your-gtrae)</p>
<p>### Güvenli Kullanım Tablosu<br />
Eklenti durumuna göre yetki ve risk dağılımı şu şekildedir:</p>
<p>| Durum | Web Sayfası Okuma | Veri Sızdırma | Geçmiş Veri Erişimi |<br />
| :&#8212; | :&#8212; | :&#8212; | :&#8212; |<br />
| **Aktif** | Tam Yetki  [reddit](https://www.reddit.com/r/chrome/comments/1ckcd1s/can_chrome_extensions_scrape_my_pages_and_steal/) | Mümkün  [youtube](https://www.youtube.com/watch?v=Rhxz_i8rDcM) | Sürekli Erişim |<br />
| **Devre Dışı** | Engellenir  [support.google](https://support.google.com/chrome/thread/11119336/can-chrome-extensions-affect-computer-performance-even-if-they-are-turned-off?hl=en) | Çok Düşük (Kalıntı Betik) | Saldırganın Sunucusunda Saklı |<br />
| **Kaldırılmış** | İmkansız | İmkansız | Saldırganın Sunucusunda Saklı |</p>
<p>Şüphelendiğiniz bir eklenti varsa sadece devre dışı bırakmak yerine doğrudan **kaldırmanız** ve ardından tarayıcı çerezlerini temizleyerek şifrelerinizi değiştirmeniz en güvenli yoldur. [thehackernews](https://thehackernews.com/2026/02/malicious-chrome-extensions-caught.html)</p>
<p>Hangi eklenti hakkında bu endişeyi taşıyorsunuz?</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
