ArchiMate -- стандарт The Open Group для моделирования enterprise-архитектуры. Описывает бизнес, приложения и инфраструктуру в единой модели. Позволяет видеть связь между бизнес-целями и технической реализацией.
Зачем нужен ArchiMate
Проблема
Как решает ArchiMate
Бизнес не понимает IT-затраты
Показывает связь бизнес-процессов и систем
IT не понимает приоритеты бизнеса
Визуализирует влияние на бизнес-цели
Нет общей картины enterprise
Единая модель всех слоёв
Сложно оценить impact изменений
Impact analysis через связи элементов
Дублирование систем
Видимость всех систем и их функций
Когда использовать ArchiMate
Ситуация
Подходит
Enterprise Architecture (EA)
Да -- основное назначение
Планирование IT-трансформации
Да
Связь бизнес-целей и IT-систем
Да
Impact analysis при изменениях
Да
Архитектура одного приложения
Нет (используй C4)
Дизайн кода/классов
Нет (используй UML)
Бизнес-процесс в деталях
Нет (используй BPMN)
Три основных слоя
ArchiMate организует enterprise в три слоя, каждый со своими элементами:
┌─────────────────────────────────────────────────────┐
│ Strategy Layer │
│ Resource, Capability, Value Stream, Course of Action│
├─────────────────────────────────────────────────────┤
│ Business Layer │
│ Actor, Role, Process, Service, Event, Object │
├─────────────────────────────────────────────────────┤
│ Application Layer │
│ Component, Interface, Function, Service, Data Object│
├─────────────────────────────────────────────────────┤
│ Technology Layer │
│ Node, Device, System Software, Artifact, Network │
├─────────────────────────────────────────────────────┤
│ Implementation & Migration │
│ Work Package, Deliverable, Plateau, Gap │
└─────────────────────────────────────────────────────┘
Business Layer
Описывает бизнес-процессы, участников и сервисы, предоставляемые организацией.
Элемент
Описание
Пример
Business Actor
Участник (организация, человек)
Клиент, Поставщик
Business Role
Роль, выполняемая актором
Менеджер продаж, Бухгалтер
Business Process
Бизнес-процесс
Обработка заказа, Найм сотрудника
Business Service
Сервис для внешних потребителей
Доставка, Поддержка клиентов
Business Event
Событие, запускающее процесс
Получение заявки, Жалоба клиента
Business Object
Бизнес-данные
Заказ, Контракт, Счёт
Contract
Соглашение между участниками
SLA, Партнёрское соглашение
Product
Набор сервисов для клиента
Подписка Premium, Тариф Enterprise
Application Layer
Описывает приложения, компоненты и данные, поддерживающие бизнес.
Элемент
Описание
Пример
Application Component
Приложение или модуль
CRM, ERP, Order Service
Application Interface
Точка доступа к приложению
REST API, Web UI, CLI
Application Function
Внутренняя функция
Расчёт стоимости, Генерация отчёта
Application Service
Сервис, предоставляемый приложением
Управление клиентами, Биллинг
Data Object
Структура данных
Customer Entity, Order Record
Application Event
Событие в приложении
OrderCreated, PaymentReceived
Application Process
Автоматизированный процесс
ETL, Batch Processing
Technology Layer
Описывает инфраструктуру: серверы, сети, ПО.
Элемент
Описание
Пример
Node
Вычислительный ресурс
Application Server, Docker Host
Device
Физическое устройство
Сервер Dell, Firewall, Load Balancer
System Software
ПО платформы
Linux, PostgreSQL, Nginx, PHP-FPM
Technology Interface
Точка подключения
HTTP/443, TCP/5432
Technology Service
Инфраструктурный сервис
Hosting, CDN, DNS
Artifact
Артефакт деплоя
Docker Image, WAR-файл, Config
Communication Network
Сеть
LAN, VPN, Internet
Path
Канал связи
HTTPS, gRPC, AMQP
Связи (Relationships)
ArchiMate определяет типизированные связи между элементами.
Структурные связи
Связь
Символ
Описание
Пример
Composition
Заполненный ромб
Часть-целое (strong)
CRM содержит модуль контактов
Aggregation
Пустой ромб
Группировка (weak)
Платформа включает несколько сервисов
Assignment
Точки + стрелка
Кто выполняет
Сервер назначен приложению
Realization
Пунктир + треугольник
Реализует
Компонент реализует сервис
Динамические связи
Связь
Символ
Описание
Пример
Serving
Сплошная стрелка
Обслуживает
API обслуживает клиента
Access
Пунктирная стрелка
Доступ к данным
Процесс читает/пишет Order
Flow
Пунктир + треугольник
Поток данных/управления
Заказ передаётся на склад
Triggering
Сплошная стрелка + заливка
Запускает
Событие запускает процесс
Другие связи
Связь
Описание
Пример
Influence
Влияние (с +/-)
Новая система улучшает производительность
Association
Общая связь
Связь между элементами без типизации
Specialization
Наследование
Premium Customer специализирует Customer
Viewpoints (Точки зрения)
Viewpoints определяют, какие элементы и связи показывать для конкретной аудитории.
<?php
declare(strict_types=1);
namespace App\Architecture;
/**
* ArchiMate layer types.
*/
enum ArchimateLayer: string
{
case Strategy = 'strategy';
case Business = 'business';
case Application = 'application';
case Technology = 'technology';
case Implementation = 'implementation';
}
/**
* Relationship types between ArchiMate elements.
*/
enum RelationshipType: string
{
case Composition = 'composition';
case Aggregation = 'aggregation';
case Assignment = 'assignment';
case Realization = 'realization';
case Serving = 'serving';
case Access = 'access';
case Flow = 'flow';
case Triggering = 'triggering';
case Influence = 'influence';
case Specialization = 'specialization';
case Association = 'association';
}
/**
* Access type for Access relationships.
*/
enum AccessType: string
{
case Read = 'read';
case Write = 'write';
case ReadWrite = 'read_write';
}
/**
* An element in the ArchiMate model.
*/
final class ArchimateElement
{
/** @var array<string, mixed> */
private array $properties = [];
public function __construct(
public readonly string $id,
public readonly string $name,
public readonly string $type,
public readonly ArchimateLayer $layer,
public readonly ?string $description = null,
) {}
public function setProperty(string $key, mixed $value): void
{
$this->properties[$key] = $value;
}
public function getProperty(string $key): mixed
{
return $this->properties[$key] ?? null;
}
/** @return array<string, mixed> */
public function getProperties(): array
{
return $this->properties;
}
}
/**
* A relationship between two ArchiMate elements.
*/
final readonly class ArchimateRelationship
{
public function __construct(
public string $id,
public RelationshipType $type,
public string $sourceId,
public string $targetId,
public ?string $label = null,
public ?AccessType $accessType = null,
) {}
}
package architecture
// ArchimateLayer represents the layers of the ArchiMate model.
type ArchimateLayer string
const (
LayerStrategy ArchimateLayer = "strategy"
LayerBusiness ArchimateLayer = "business"
LayerApplication ArchimateLayer = "application"
LayerTechnology ArchimateLayer = "technology"
LayerImplementation ArchimateLayer = "implementation"
)
// RelationshipType represents ArchiMate relationship types.
type RelationshipType string
const (
RelComposition RelationshipType = "composition"
RelAggregation RelationshipType = "aggregation"
RelAssignment RelationshipType = "assignment"
RelRealization RelationshipType = "realization"
RelServing RelationshipType = "serving"
RelAccess RelationshipType = "access"
RelFlow RelationshipType = "flow"
RelTriggering RelationshipType = "triggering"
RelInfluence RelationshipType = "influence"
RelSpecialization RelationshipType = "specialization"
RelAssociation RelationshipType = "association"
)
// AccessType specifies access direction for Access relationships.
type AccessType string
const (
AccessRead AccessType = "read"
AccessWrite AccessType = "write"
AccessReadWrite AccessType = "read_write"
)
// ArchimateElement represents an element in the ArchiMate model.
type ArchimateElement struct {
ID string
Name string
Type string
Layer ArchimateLayer
Description string
Properties map[string]any
}
// NewArchimateElement creates an element with initialized properties.
func NewArchimateElement(id, name, elemType string, layer ArchimateLayer, description string) *ArchimateElement {
return &ArchimateElement{
ID: id, Name: name, Type: elemType, Layer: layer,
Description: description, Properties: make(map[string]any),
}
}
// SetProperty sets a custom property on the element.
func (e *ArchimateElement) SetProperty(key string, value any) {
e.Properties[key] = value
}
// GetProperty retrieves a custom property.
func (e *ArchimateElement) GetProperty(key string) any {
return e.Properties[key]
}
// ArchimateRelationship represents a relationship between elements.
type ArchimateRelationship struct {
ID string
Type RelationshipType
SourceID string
TargetID string
Label string
AccessType AccessType
}
namespace App.Architecture;
/// ArchiMate layer types.
public enum ArchimateLayer
{
Strategy,
Business,
Application,
Technology,
Implementation,
}
/// Relationship types between ArchiMate elements.
public enum RelationshipType
{
Composition,
Aggregation,
Assignment,
Realization,
Serving,
Access,
Flow,
Triggering,
Influence,
Specialization,
Association,
}
/// Access direction for Access relationships.
public enum AccessType
{
Read,
Write,
ReadWrite,
}
/// An element in the ArchiMate model.
public sealed class ArchimateElement
{
private readonly Dictionary<string, object> _properties = [];
public ArchimateElement(
string id,
string name,
string type,
ArchimateLayer layer,
string? description = null)
{
Id = id;
Name = name;
Type = type;
Layer = layer;
Description = description;
}
public string Id { get; }
public string Name { get; }
public string Type { get; }
public ArchimateLayer Layer { get; }
public string? Description { get; }
public IReadOnlyDictionary<string, object> Properties => _properties;
public void SetProperty(string key, object value) => _properties[key] = value;
public object? GetProperty(string key) => _properties.GetValueOrDefault(key);
}
/// A relationship between two ArchiMate elements.
public sealed record ArchimateRelationship(
string Id,
RelationshipType Type,
string SourceId,
string TargetId,
string? Label = null,
AccessType? AccessType = null);
from dataclasses import dataclass, field
from enum import Enum
from typing import Any
class ArchimateLayer(str, Enum):
"""ArchiMate layer types."""
STRATEGY = "strategy"
BUSINESS = "business"
APPLICATION = "application"
TECHNOLOGY = "technology"
IMPLEMENTATION = "implementation"
class RelationshipType(str, Enum):
"""Relationship types between ArchiMate elements."""
COMPOSITION = "composition"
AGGREGATION = "aggregation"
ASSIGNMENT = "assignment"
REALIZATION = "realization"
SERVING = "serving"
ACCESS = "access"
FLOW = "flow"
TRIGGERING = "triggering"
INFLUENCE = "influence"
SPECIALIZATION = "specialization"
ASSOCIATION = "association"
class AccessType(str, Enum):
"""Access direction for Access relationships."""
READ = "read"
WRITE = "write"
READ_WRITE = "read_write"
@dataclass(slots=True)
class ArchimateElement:
"""An element in the ArchiMate model."""
id: str
name: str
type: str
layer: ArchimateLayer
description: str | None = None
properties: dict[str, Any] = field(default_factory=dict)
def set_property(self, key: str, value: Any) -> None:
self.properties[key] = value
def get_property(self, key: str) -> Any | None:
return self.properties.get(key)
@dataclass(frozen=True, slots=True)
class ArchimateRelationship:
"""A relationship between two ArchiMate elements."""
id: str
type: RelationshipType
source_id: str
target_id: str
label: str | None = None
access_type: AccessType | None = None
### Enterprise Architecture Model
<?php
declare(strict_types=1);
namespace App\Architecture;
/**
* Enterprise Architecture model following ArchiMate standard.
* Manages elements, relationships, and provides analysis capabilities.
*/
final class EnterpriseArchitectureModel
{
/** @var array<string, ArchimateElement> */
private array $elements = [];
/** @var array<string, ArchimateRelationship> */
private array $relationships = [];
/**
* Add an element to the model.
*/
public function addElement(ArchimateElement $element): void
{
if (isset($this->elements[$element->id])) {
throw new \DomainException(
sprintf('Element with ID "%s" already exists', $element->id),
);
}
$this->elements[$element->id] = $element;
}
/**
* Add a relationship between two elements.
*/
public function addRelationship(ArchimateRelationship $relationship): void
{
$this->validateElementExists($relationship->sourceId);
$this->validateElementExists($relationship->targetId);
$this->validateRelationship($relationship);
$this->relationships[$relationship->id] = $relationship;
}
/**
* Impact analysis: find all elements affected by changing a given element.
* Traverses serving, realization, and triggering relationships.
*
* @return array<ArchimateElement>
*/
public function impactAnalysis(string $elementId): array
{
$this->validateElementExists($elementId);
$visited = [];
$impacted = [];
$this->traverseImpact($elementId, $visited, $impacted);
return $impacted;
}
/**
* Get all elements in a specific layer.
*
* @return array<ArchimateElement>
*/
public function getElementsByLayer(ArchimateLayer $layer): array
{
return array_filter(
$this->elements,
static fn(ArchimateElement $e) => $e->layer === $layer,
);
}
/**
* Get elements that serve (support) a given element.
*
* @return array<ArchimateElement>
*/
public function getSupportingElements(string $elementId): array
{
$this->validateElementExists($elementId);
$supporting = [];
foreach ($this->relationships as $rel) {
if ($rel->targetId === $elementId && $rel->type === RelationshipType::Serving) {
$supporting[] = $this->elements[$rel->sourceId];
}
}
return $supporting;
}
/**
* Get elements that realize a given element.
*
* @return array<ArchimateElement>
*/
public function getRealizingElements(string $elementId): array
{
$this->validateElementExists($elementId);
$realizing = [];
foreach ($this->relationships as $rel) {
if ($rel->targetId === $elementId && $rel->type === RelationshipType::Realization) {
$realizing[] = $this->elements[$rel->sourceId];
}
}
return $realizing;
}
/**
* Dependency analysis: find all elements that this element depends on.
*
* @return array<ArchimateElement>
*/
public function getDependencies(string $elementId): array
{
$this->validateElementExists($elementId);
$dependencies = [];
$dependencyTypes = [
RelationshipType::Serving,
RelationshipType::Realization,
RelationshipType::Access,
];
foreach ($this->relationships as $rel) {
if ($rel->sourceId === $elementId && in_array($rel->type, $dependencyTypes, true)) {
$dependencies[] = $this->elements[$rel->targetId];
}
}
return $dependencies;
}
/**
* Generate a layered view of the architecture as text.
*/
public function generateLayeredView(): string
{
$output = [];
foreach (ArchimateLayer::cases() as $layer) {
$elements = $this->getElementsByLayer($layer);
if (empty($elements)) {
continue;
}
$output[] = sprintf('=== %s Layer ===', ucfirst($layer->value));
foreach ($elements as $element) {
$output[] = sprintf(
' [%s] %s (%s)',
$element->type,
$element->name,
$element->id,
);
// Show outgoing relationships
foreach ($this->relationships as $rel) {
if ($rel->sourceId === $element->id) {
$target = $this->elements[$rel->targetId];
$output[] = sprintf(
' --%s--> %s',
$rel->type->value,
$target->name,
);
}
}
}
$output[] = '';
}
return implode("\n", $output);
}
/**
* Recursive traversal for impact analysis.
*
* @param array<string, bool> $visited
* @param array<ArchimateElement> $impacted
*/
private function traverseImpact(string $elementId, array &$visited, array &$impacted): void
{
if (isset($visited[$elementId])) {
return;
}
$visited[$elementId] = true;
$impactTypes = [
RelationshipType::Serving,
RelationshipType::Realization,
RelationshipType::Triggering,
RelationshipType::Composition,
RelationshipType::Flow,
];
foreach ($this->relationships as $rel) {
// Elements that depend on the changed element
if ($rel->sourceId === $elementId && in_array($rel->type, $impactTypes, true)) {
if (!isset($visited[$rel->targetId])) {
$impacted[] = $this->elements[$rel->targetId];
$this->traverseImpact($rel->targetId, $visited, $impacted);
}
}
// Elements that are served by the changed element
if ($rel->targetId === $elementId && $rel->type === RelationshipType::Serving) {
if (!isset($visited[$rel->sourceId])) {
$impacted[] = $this->elements[$rel->sourceId];
$this->traverseImpact($rel->sourceId, $visited, $impacted);
}
}
}
}
/**
* Validate allowed relationships between layers.
* ArchiMate restricts certain cross-layer relationships.
*/
private function validateRelationship(ArchimateRelationship $relationship): void
{
$source = $this->elements[$relationship->sourceId];
$target = $this->elements[$relationship->targetId];
// Serving relationships typically go upward (lower layer serves upper)
if ($relationship->type === RelationshipType::Serving) {
$layerOrder = [
ArchimateLayer::Technology->value => 0,
ArchimateLayer::Application->value => 1,
ArchimateLayer::Business->value => 2,
ArchimateLayer::Strategy->value => 3,
];
$sourceOrder = $layerOrder[$source->layer->value] ?? 0;
$targetOrder = $layerOrder[$target->layer->value] ?? 0;
if ($sourceOrder > $targetOrder) {
throw new \DomainException(
sprintf(
'Serving relationship from %s layer to %s layer is not typical in ArchiMate',
$source->layer->value,
$target->layer->value,
),
);
}
}
}
private function validateElementExists(string $elementId): void
{
if (!isset($this->elements[$elementId])) {
throw new \DomainException(
sprintf('Element "%s" not found in model', $elementId),
);
}
}
}
package architecture
import (
"fmt"
"strings"
)
// EnterpriseArchitectureModel manages ArchiMate elements and relationships.
type EnterpriseArchitectureModel struct {
elements map[string]*ArchimateElement
relationships map[string]*ArchimateRelationship
}
// NewEnterpriseArchitectureModel creates an empty model.
func NewEnterpriseArchitectureModel() *EnterpriseArchitectureModel {
return &EnterpriseArchitectureModel{
elements: make(map[string]*ArchimateElement),
relationships: make(map[string]*ArchimateRelationship),
}
}
// AddElement adds an element to the model.
func (m *EnterpriseArchitectureModel) AddElement(e *ArchimateElement) error {
if _, exists := m.elements[e.ID]; exists {
return fmt.Errorf("element with ID %q already exists", e.ID)
}
m.elements[e.ID] = e
return nil
}
// AddRelationship adds a relationship between two elements.
func (m *EnterpriseArchitectureModel) AddRelationship(r *ArchimateRelationship) error {
if _, ok := m.elements[r.SourceID]; !ok {
return fmt.Errorf("element %q not found", r.SourceID)
}
if _, ok := m.elements[r.TargetID]; !ok {
return fmt.Errorf("element %q not found", r.TargetID)
}
m.relationships[r.ID] = r
return nil
}
// ImpactAnalysis finds all elements affected by changing a given element.
func (m *EnterpriseArchitectureModel) ImpactAnalysis(elementID string) ([]*ArchimateElement, error) {
if _, ok := m.elements[elementID]; !ok {
return nil, fmt.Errorf("element %q not found", elementID)
}
visited := make(map[string]bool)
var impacted []*ArchimateElement
m.traverseImpact(elementID, visited, &impacted)
return impacted, nil
}
// GetElementsByLayer returns elements belonging to a specific layer.
func (m *EnterpriseArchitectureModel) GetElementsByLayer(layer ArchimateLayer) []*ArchimateElement {
var result []*ArchimateElement
for _, e := range m.elements {
if e.Layer == layer {
result = append(result, e)
}
}
return result
}
// GetSupportingElements returns elements that serve the given element.
func (m *EnterpriseArchitectureModel) GetSupportingElements(elementID string) []*ArchimateElement {
var result []*ArchimateElement
for _, r := range m.relationships {
if r.TargetID == elementID && r.Type == RelServing {
result = append(result, m.elements[r.SourceID])
}
}
return result
}
// GenerateLayeredView produces a text representation of the architecture.
func (m *EnterpriseArchitectureModel) GenerateLayeredView() string {
layers := []ArchimateLayer{LayerStrategy, LayerBusiness, LayerApplication, LayerTechnology, LayerImplementation}
var sb strings.Builder
for _, layer := range layers {
elems := m.GetElementsByLayer(layer)
if len(elems) == 0 {
continue
}
sb.WriteString(fmt.Sprintf("=== %s Layer ===\n", strings.Title(string(layer))))
for _, e := range elems {
sb.WriteString(fmt.Sprintf(" [%s] %s (%s)\n", e.Type, e.Name, e.ID))
for _, r := range m.relationships {
if r.SourceID == e.ID {
target := m.elements[r.TargetID]
sb.WriteString(fmt.Sprintf(" --%s--> %s\n", r.Type, target.Name))
}
}
}
sb.WriteString("\n")
}
return sb.String()
}
func (m *EnterpriseArchitectureModel) traverseImpact(elementID string, visited map[string]bool, impacted *[]*ArchimateElement) {
if visited[elementID] {
return
}
visited[elementID] = true
impactTypes := map[RelationshipType]bool{
RelServing: true, RelRealization: true, RelTriggering: true,
RelComposition: true, RelFlow: true,
}
for _, r := range m.relationships {
if r.SourceID == elementID && impactTypes[r.Type] {
if !visited[r.TargetID] {
*impacted = append(*impacted, m.elements[r.TargetID])
m.traverseImpact(r.TargetID, visited, impacted)
}
}
if r.TargetID == elementID && r.Type == RelServing {
if !visited[r.SourceID] {
*impacted = append(*impacted, m.elements[r.SourceID])
m.traverseImpact(r.SourceID, visited, impacted)
}
}
}
}
using System.Text;
namespace App.Architecture;
/// Raised when the model is left in an invalid state.
public sealed class ArchitectureModelException : Exception
{
public ArchitectureModelException(string message) : base(message) { }
}
/// Enterprise Architecture model following the ArchiMate standard.
/// Manages elements, relationships and provides analysis capabilities.
public sealed class EnterpriseArchitectureModel
{
private static readonly HashSet<RelationshipType> DependencyTypes =
[
RelationshipType.Serving,
RelationshipType.Realization,
RelationshipType.Access,
];
private static readonly HashSet<RelationshipType> ImpactTypes =
[
RelationshipType.Serving,
RelationshipType.Realization,
RelationshipType.Triggering,
RelationshipType.Composition,
RelationshipType.Flow,
];
// Lower number = lower layer. Serving flows upward in ArchiMate.
private static readonly IReadOnlyDictionary<ArchimateLayer, int> LayerOrder =
new Dictionary<ArchimateLayer, int>
{
[ArchimateLayer.Technology] = 0,
[ArchimateLayer.Application] = 1,
[ArchimateLayer.Business] = 2,
[ArchimateLayer.Strategy] = 3,
};
private readonly Dictionary<string, ArchimateElement> _elements = [];
private readonly Dictionary<string, ArchimateRelationship> _relationships = [];
// Add an element to the model.
public void AddElement(ArchimateElement element)
{
if (!_elements.TryAdd(element.Id, element))
{
throw new ArchitectureModelException($"Element with ID \"{element.Id}\" already exists");
}
}
// Add a relationship between two elements.
public void AddRelationship(ArchimateRelationship relationship)
{
var source = RequireElement(relationship.SourceId);
var target = RequireElement(relationship.TargetId);
ValidateRelationship(relationship, source, target);
_relationships[relationship.Id] = relationship;
}
// Impact analysis: find all elements affected by changing a given element.
public IReadOnlyList<ArchimateElement> ImpactAnalysis(string elementId)
{
RequireElement(elementId);
var visited = new HashSet<string>();
var impacted = new List<ArchimateElement>();
TraverseImpact(elementId, visited, impacted);
return impacted;
}
// Get all elements in a specific layer.
public IReadOnlyList<ArchimateElement> GetElementsByLayer(ArchimateLayer layer)
=> _elements.Values.Where(e => e.Layer == layer).ToArray();
// Get elements that serve (support) a given element.
public IReadOnlyList<ArchimateElement> GetSupportingElements(string elementId)
=> RelatedSources(elementId, type => type is RelationshipType.Serving);
// Get elements that realize a given element.
public IReadOnlyList<ArchimateElement> GetRealizingElements(string elementId)
=> RelatedSources(elementId, type => type is RelationshipType.Realization);
// Dependency analysis: elements this element depends on.
public IReadOnlyList<ArchimateElement> GetDependencies(string elementId)
{
RequireElement(elementId);
return _relationships.Values
.Where(rel => rel.SourceId == elementId && DependencyTypes.Contains(rel.Type))
.Select(rel => _elements[rel.TargetId])
.ToArray();
}
// Generate a layered view of the architecture as text.
public string GenerateLayeredView()
{
var builder = new StringBuilder();
foreach (var layer in Enum.GetValues<ArchimateLayer>())
{
var elements = GetElementsByLayer(layer);
if (elements.Count == 0)
{
continue;
}
builder.AppendLine($"=== {layer} Layer ===");
foreach (var element in elements)
{
builder.AppendLine($" [{element.Type}] {element.Name} ({element.Id})");
// Show outgoing relationships
foreach (var rel in _relationships.Values.Where(r => r.SourceId == element.Id))
{
builder.AppendLine($" --{rel.Type}--> {_elements[rel.TargetId].Name}");
}
}
builder.AppendLine();
}
return builder.ToString();
}
private IReadOnlyList<ArchimateElement> RelatedSources(
string elementId,
Func<RelationshipType, bool> matches)
{
RequireElement(elementId);
return _relationships.Values
.Where(rel => rel.TargetId == elementId && matches(rel.Type))
.Select(rel => _elements[rel.SourceId])
.ToArray();
}
// Recursive traversal for impact analysis.
private void TraverseImpact(string elementId, HashSet<string> visited, List<ArchimateElement> impacted)
{
if (!visited.Add(elementId))
{
return;
}
foreach (var rel in _relationships.Values)
{
// Elements that depend on the changed element
if (rel.SourceId == elementId && ImpactTypes.Contains(rel.Type) && !visited.Contains(rel.TargetId))
{
impacted.Add(_elements[rel.TargetId]);
TraverseImpact(rel.TargetId, visited, impacted);
}
// Elements that are served by the changed element
if (rel.TargetId == elementId && rel.Type is RelationshipType.Serving && !visited.Contains(rel.SourceId))
{
impacted.Add(_elements[rel.SourceId]);
TraverseImpact(rel.SourceId, visited, impacted);
}
}
}
/// Validate allowed relationships between layers.
/// ArchiMate restricts certain cross-layer relationships.
private static void ValidateRelationship(
ArchimateRelationship relationship,
ArchimateElement source,
ArchimateElement target)
{
if (relationship.Type is not RelationshipType.Serving)
{
return;
}
// Serving relationships typically go upward (lower layer serves upper)
var sourceOrder = LayerOrder.GetValueOrDefault(source.Layer);
var targetOrder = LayerOrder.GetValueOrDefault(target.Layer);
if (sourceOrder > targetOrder)
{
throw new ArchitectureModelException(
$"Serving relationship from {source.Layer} layer to {target.Layer} layer " +
"is not typical in ArchiMate");
}
}
private ArchimateElement RequireElement(string elementId)
=> _elements.TryGetValue(elementId, out var element)
? element
: throw new ArchitectureModelException($"Element \"{elementId}\" not found in model");
}
from typing import Callable, Iterable
DEPENDENCY_TYPES = frozenset(
{
RelationshipType.SERVING,
RelationshipType.REALIZATION,
RelationshipType.ACCESS,
}
)
IMPACT_TYPES = frozenset(
{
RelationshipType.SERVING,
RelationshipType.REALIZATION,
RelationshipType.TRIGGERING,
RelationshipType.COMPOSITION,
RelationshipType.FLOW,
}
)
# Lower number = lower layer. Serving flows upward in ArchiMate.
LAYER_ORDER = {
ArchimateLayer.TECHNOLOGY: 0,
ArchimateLayer.APPLICATION: 1,
ArchimateLayer.BUSINESS: 2,
ArchimateLayer.STRATEGY: 3,
}
class ArchitectureModelError(Exception):
"""Raised when the model would be left in an invalid state."""
class EnterpriseArchitectureModel:
"""Enterprise Architecture model following the ArchiMate standard.
Manages elements, relationships and provides analysis capabilities.
"""
def __init__(self) -> None:
self._elements: dict[str, ArchimateElement] = {}
self._relationships: dict[str, ArchimateRelationship] = {}
def add_element(self, element: ArchimateElement) -> None:
"""Add an element to the model."""
if element.id in self._elements:
raise ArchitectureModelError(f'Element with ID "{element.id}" already exists')
self._elements[element.id] = element
def add_relationship(self, relationship: ArchimateRelationship) -> None:
"""Add a relationship between two elements."""
source = self._require_element(relationship.source_id)
target = self._require_element(relationship.target_id)
self._validate_relationship(relationship, source, target)
self._relationships[relationship.id] = relationship
def impact_analysis(self, element_id: str) -> list[ArchimateElement]:
"""Find all elements affected by changing a given element."""
self._require_element(element_id)
visited: set[str] = set()
impacted: list[ArchimateElement] = []
self._traverse_impact(element_id, visited, impacted)
return impacted
def get_elements_by_layer(self, layer: ArchimateLayer) -> list[ArchimateElement]:
"""Get all elements in a specific layer."""
return [e for e in self._elements.values() if e.layer is layer]
def get_supporting_elements(self, element_id: str) -> list[ArchimateElement]:
"""Get elements that serve (support) a given element."""
return self._related_sources(
element_id, lambda rel_type: rel_type is RelationshipType.SERVING
)
def get_realizing_elements(self, element_id: str) -> list[ArchimateElement]:
"""Get elements that realize a given element."""
return self._related_sources(
element_id, lambda rel_type: rel_type is RelationshipType.REALIZATION
)
def get_dependencies(self, element_id: str) -> list[ArchimateElement]:
"""Find all elements this element depends on."""
self._require_element(element_id)
return [
self._elements[rel.target_id]
for rel in self._relationships.values()
if rel.source_id == element_id and rel.type in DEPENDENCY_TYPES
]
def generate_layered_view(self) -> str:
"""Generate a layered view of the architecture as text."""
lines: list[str] = []
for layer in ArchimateLayer:
elements = self.get_elements_by_layer(layer)
if not elements:
continue
lines.append(f"=== {layer.value.capitalize()} Layer ===")
for element in elements:
lines.append(f" [{element.type}] {element.name} ({element.id})")
# Show outgoing relationships
for rel in self._outgoing(element.id):
target = self._elements[rel.target_id]
lines.append(f" --{rel.type.value}--> {target.name}")
lines.append("")
return "\n".join(lines)
def _related_sources(
self,
element_id: str,
matches: Callable[[RelationshipType], bool],
) -> list[ArchimateElement]:
self._require_element(element_id)
return [
self._elements[rel.source_id]
for rel in self._relationships.values()
if rel.target_id == element_id and matches(rel.type)
]
def _outgoing(self, element_id: str) -> Iterable[ArchimateRelationship]:
return (rel for rel in self._relationships.values() if rel.source_id == element_id)
def _traverse_impact(
self,
element_id: str,
visited: set[str],
impacted: list[ArchimateElement],
) -> None:
"""Recursive traversal for impact analysis."""
if element_id in visited:
return
visited.add(element_id)
for rel in self._relationships.values():
# Elements that depend on the changed element
if (
rel.source_id == element_id
and rel.type in IMPACT_TYPES
and rel.target_id not in visited
):
impacted.append(self._elements[rel.target_id])
self._traverse_impact(rel.target_id, visited, impacted)
# Elements that are served by the changed element
if (
rel.target_id == element_id
and rel.type is RelationshipType.SERVING
and rel.source_id not in visited
):
impacted.append(self._elements[rel.source_id])
self._traverse_impact(rel.source_id, visited, impacted)
@staticmethod
def _validate_relationship(
relationship: ArchimateRelationship,
source: ArchimateElement,
target: ArchimateElement,
) -> None:
"""Validate allowed relationships between layers.
ArchiMate restricts certain cross-layer relationships.
"""
if relationship.type is not RelationshipType.SERVING:
return
# Serving relationships typically go upward (lower layer serves upper)
if LAYER_ORDER.get(source.layer, 0) > LAYER_ORDER.get(target.layer, 0):
raise ArchitectureModelError(
f"Serving relationship from {source.layer.value} layer to "
f"{target.layer.value} layer is not typical in ArchiMate"
)
def _require_element(self, element_id: str) -> ArchimateElement:
element = self._elements.get(element_id)
if element is None:
raise ArchitectureModelError(f'Element "{element_id}" not found in model')
return element
namespace App.Architecture;
/// Example: building an ArchiMate model for an e-commerce system.
public sealed class EcommerceModelBuilder
{
public EnterpriseArchitectureModel Build()
{
var model = new EnterpriseArchitectureModel();
// --- Business Layer ---
model.AddElement(new ArchimateElement(
"ba-customer", "Customer", "BusinessActor", ArchimateLayer.Business));
model.AddElement(new ArchimateElement(
"bp-order",
"Order Processing",
"BusinessProcess",
ArchimateLayer.Business,
"End-to-end order processing from placement to delivery"));
model.AddElement(new ArchimateElement(
"bs-delivery", "Delivery Service", "BusinessService", ArchimateLayer.Business));
// --- Application Layer ---
model.AddElement(new ArchimateElement(
"ac-webshop", "Web Shop", "ApplicationComponent", ArchimateLayer.Application));
model.AddElement(new ArchimateElement(
"ac-orderservice", "Order Service", "ApplicationComponent", ArchimateLayer.Application));
model.AddElement(new ArchimateElement(
"ai-restapi", "REST API", "ApplicationInterface", ArchimateLayer.Application));
model.AddElement(new ArchimateElement(
"do-order", "Order Data", "DataObject", ArchimateLayer.Application));
// --- Technology Layer ---
model.AddElement(new ArchimateElement(
"tn-k8s", "Kubernetes Cluster", "Node", ArchimateLayer.Technology));
model.AddElement(new ArchimateElement(
"ts-dotnet", ".NET 9 Runtime", "SystemSoftware", ArchimateLayer.Technology));
model.AddElement(new ArchimateElement(
"ts-postgres", "PostgreSQL 18", "SystemSoftware", ArchimateLayer.Technology));
// --- Relationships ---
// Business: Customer triggers Order Processing
model.AddRelationship(new ArchimateRelationship(
"r1", RelationshipType.Triggering, "ba-customer", "bp-order"));
// Application realizes Business
model.AddRelationship(new ArchimateRelationship(
"r2", RelationshipType.Realization, "ac-orderservice", "bp-order", Label: "automates"));
// Web Shop serves Customer
model.AddRelationship(new ArchimateRelationship(
"r3", RelationshipType.Serving, "ac-webshop", "ba-customer"));
// Order Service accesses Order Data
model.AddRelationship(new ArchimateRelationship(
"r4",
RelationshipType.Access,
"ac-orderservice",
"do-order",
AccessType: AccessType.ReadWrite));
// Technology realizes Application
model.AddRelationship(new ArchimateRelationship(
"r5", RelationshipType.Realization, "ts-dotnet", "ac-orderservice"));
model.AddRelationship(new ArchimateRelationship(
"r6", RelationshipType.Realization, "ts-postgres", "do-order"));
// K8s contains the .NET runtime (composition)
model.AddRelationship(new ArchimateRelationship(
"r7", RelationshipType.Composition, "tn-k8s", "ts-dotnet"));
return model;
}
}
def build_ecommerce_model() -> EnterpriseArchitectureModel:
"""Build an ArchiMate model for an e-commerce system."""
model = EnterpriseArchitectureModel()
# --- Business Layer ---
model.add_element(
ArchimateElement(
id="ba-customer",
name="Customer",
type="BusinessActor",
layer=ArchimateLayer.BUSINESS,
)
)
model.add_element(
ArchimateElement(
id="bp-order",
name="Order Processing",
type="BusinessProcess",
layer=ArchimateLayer.BUSINESS,
description="End-to-end order processing from placement to delivery",
)
)
model.add_element(
ArchimateElement(
id="bs-delivery",
name="Delivery Service",
type="BusinessService",
layer=ArchimateLayer.BUSINESS,
)
)
# --- Application Layer ---
for element_id, name, element_type in (
("ac-webshop", "Web Shop", "ApplicationComponent"),
("ac-orderservice", "Order Service", "ApplicationComponent"),
("ai-restapi", "REST API", "ApplicationInterface"),
("do-order", "Order Data", "DataObject"),
):
model.add_element(
ArchimateElement(
id=element_id,
name=name,
type=element_type,
layer=ArchimateLayer.APPLICATION,
)
)
# --- Technology Layer ---
for element_id, name, element_type in (
("tn-k8s", "Kubernetes Cluster", "Node"),
("ts-python", "Python 3.13 / Uvicorn", "SystemSoftware"),
("ts-postgres", "PostgreSQL 18", "SystemSoftware"),
):
model.add_element(
ArchimateElement(
id=element_id,
name=name,
type=element_type,
layer=ArchimateLayer.TECHNOLOGY,
)
)
# --- Relationships ---
# Business: Customer triggers Order Processing
model.add_relationship(
ArchimateRelationship("r1", RelationshipType.TRIGGERING, "ba-customer", "bp-order")
)
# Application realizes Business
model.add_relationship(
ArchimateRelationship(
"r2",
RelationshipType.REALIZATION,
"ac-orderservice",
"bp-order",
label="automates",
)
)
# Web Shop serves Customer
model.add_relationship(
ArchimateRelationship("r3", RelationshipType.SERVING, "ac-webshop", "ba-customer")
)
# Order Service accesses Order Data
model.add_relationship(
ArchimateRelationship(
"r4",
RelationshipType.ACCESS,
"ac-orderservice",
"do-order",
access_type=AccessType.READ_WRITE,
)
)
# Technology realizes Application
model.add_relationship(
ArchimateRelationship("r5", RelationshipType.REALIZATION, "ts-python", "ac-orderservice")
)
model.add_relationship(
ArchimateRelationship("r6", RelationshipType.REALIZATION, "ts-postgres", "do-order")
)
# K8s contains the Python runtime (composition)
model.add_relationship(
ArchimateRelationship("r7", RelationshipType.COMPOSITION, "tn-k8s", "ts-python")
)
return model
## Impact Analysis
Одна из главных ценностей ArchiMate -- анализ влияния изменений.
<?php
declare(strict_types=1);
namespace App\Architecture;
/**
* Impact analysis report generator.
* Shows which elements are affected when a component changes.
*/
final readonly class ImpactAnalyzer
{
public function __construct(
private EnterpriseArchitectureModel $model,
) {}
/**
* Generate a human-readable impact report.
*/
public function generateReport(string $elementId): string
{
$impacted = $this->model->impactAnalysis($elementId);
if (empty($impacted)) {
return sprintf('No impact detected for element "%s".', $elementId);
}
$lines = [
sprintf('Impact Analysis for: %s', $elementId),
str_repeat('=', 50),
'',
];
// Group impacted elements by layer
$byLayer = [];
foreach ($impacted as $element) {
$byLayer[$element->layer->value][] = $element;
}
foreach ($byLayer as $layer => $elements) {
$lines[] = sprintf('[%s Layer]', ucfirst($layer));
foreach ($elements as $element) {
$lines[] = sprintf(
' - %s (%s): %s',
$element->name,
$element->type,
$element->description ?? 'No description',
);
}
$lines[] = '';
}
$lines[] = sprintf('Total affected elements: %d', count($impacted));
return implode("\n", $lines);
}
/**
* Assess risk level based on number and type of impacted elements.
*/
public function assessRisk(string $elementId): string
{
$impacted = $this->model->impactAnalysis($elementId);
$businessImpact = array_filter(
$impacted,
static fn(ArchimateElement $e) => $e->layer === ArchimateLayer::Business,
);
$totalCount = count($impacted);
$businessCount = count($businessImpact);
return match (true) {
$businessCount > 0 && $totalCount > 5 => 'CRITICAL',
$businessCount > 0 => 'HIGH',
$totalCount > 5 => 'MEDIUM',
$totalCount > 0 => 'LOW',
default => 'NONE',
};
}
}
package architecture
import (
"fmt"
"strings"
)
// ImpactAnalyzer generates impact analysis reports.
type ImpactAnalyzer struct {
model *EnterpriseArchitectureModel
}
// NewImpactAnalyzer creates an analyzer for the given model.
func NewImpactAnalyzer(model *EnterpriseArchitectureModel) *ImpactAnalyzer {
return &ImpactAnalyzer{model: model}
}
// GenerateReport produces a human-readable impact report.
func (a *ImpactAnalyzer) GenerateReport(elementID string) string {
impacted, err := a.model.ImpactAnalysis(elementID)
if err != nil || len(impacted) == 0 {
return fmt.Sprintf("No impact detected for element %q.", elementID)
}
var sb strings.Builder
sb.WriteString(fmt.Sprintf("Impact Analysis for: %s\n", elementID))
sb.WriteString(strings.Repeat("=", 50) + "\n\n")
byLayer := make(map[ArchimateLayer][]*ArchimateElement)
for _, e := range impacted {
byLayer[e.Layer] = append(byLayer[e.Layer], e)
}
for layer, elems := range byLayer {
sb.WriteString(fmt.Sprintf("[%s Layer]\n", strings.Title(string(layer))))
for _, e := range elems {
desc := e.Description
if desc == "" {
desc = "No description"
}
sb.WriteString(fmt.Sprintf(" - %s (%s): %s\n", e.Name, e.Type, desc))
}
sb.WriteString("\n")
}
sb.WriteString(fmt.Sprintf("Total affected elements: %d", len(impacted)))
return sb.String()
}
// AssessRisk returns a risk level based on impacted elements.
func (a *ImpactAnalyzer) AssessRisk(elementID string) string {
impacted, _ := a.model.ImpactAnalysis(elementID)
businessCount := 0
for _, e := range impacted {
if e.Layer == LayerBusiness {
businessCount++
}
}
total := len(impacted)
switch {
case businessCount > 0 && total > 5:
return "CRITICAL"
case businessCount > 0:
return "HIGH"
case total > 5:
return "MEDIUM"
case total > 0:
return "LOW"
default:
return "NONE"
}
}
using System.Text;
namespace App.Architecture;
public enum RiskLevel
{
None,
Low,
Medium,
High,
Critical,
}
/// Impact analysis report generator.
/// Shows which elements are affected when a component changes.
public sealed class ImpactAnalyzer
{
private const int LargeImpactThreshold = 5;
private readonly EnterpriseArchitectureModel _model;
public ImpactAnalyzer(EnterpriseArchitectureModel model) => _model = model;
// Generate a human-readable impact report.
public string GenerateReport(string elementId)
{
var impacted = _model.ImpactAnalysis(elementId);
if (impacted.Count == 0)
{
return $"No impact detected for element \"{elementId}\".";
}
var builder = new StringBuilder()
.AppendLine($"Impact Analysis for: {elementId}")
.AppendLine(new string('=', 50))
.AppendLine();
// Group impacted elements by layer
foreach (var group in impacted.GroupBy(element => element.Layer))
{
builder.AppendLine($"[{group.Key} Layer]");
foreach (var element in group)
{
builder.AppendLine(
$" - {element.Name} ({element.Type}): {element.Description ?? "No description"}");
}
builder.AppendLine();
}
builder.Append($"Total affected elements: {impacted.Count}");
return builder.ToString();
}
// Assess risk level based on the number and type of impacted elements.
public RiskLevel AssessRisk(string elementId)
{
var impacted = _model.ImpactAnalysis(elementId);
var businessCount = impacted.Count(e => e.Layer is ArchimateLayer.Business);
return (businessCount, impacted.Count) switch
{
( > 0, > LargeImpactThreshold) => RiskLevel.Critical,
( > 0, _) => RiskLevel.High,
(_, > LargeImpactThreshold) => RiskLevel.Medium,
(_, > 0) => RiskLevel.Low,
_ => RiskLevel.None,
};
}
}
from collections import defaultdict
from enum import Enum
LARGE_IMPACT_THRESHOLD = 5
class RiskLevel(str, Enum):
NONE = "NONE"
LOW = "LOW"
MEDIUM = "MEDIUM"
HIGH = "HIGH"
CRITICAL = "CRITICAL"
class ImpactAnalyzer:
"""Impact analysis report generator.
Shows which elements are affected when a component changes.
"""
def __init__(self, model: EnterpriseArchitectureModel) -> None:
self._model = model
def generate_report(self, element_id: str) -> str:
"""Generate a human-readable impact report."""
impacted = self._model.impact_analysis(element_id)
if not impacted:
return f'No impact detected for element "{element_id}".'
lines = [f"Impact Analysis for: {element_id}", "=" * 50, ""]
# Group impacted elements by layer
by_layer: dict[ArchimateLayer, list[ArchimateElement]] = defaultdict(list)
for element in impacted:
by_layer[element.layer].append(element)
for layer, elements in by_layer.items():
lines.append(f"[{layer.value.capitalize()} Layer]")
for element in elements:
description = element.description or "No description"
lines.append(f" - {element.name} ({element.type}): {description}")
lines.append("")
lines.append(f"Total affected elements: {len(impacted)}")
return "\n".join(lines)
def assess_risk(self, element_id: str) -> RiskLevel:
"""Assess risk level based on the number and type of impacted elements."""
impacted = self._model.impact_analysis(element_id)
business_count = sum(1 for e in impacted if e.layer is ArchimateLayer.BUSINESS)
total = len(impacted)
# match/case on a tuple mirrors PHP's match(true) chain
match (business_count > 0, total > LARGE_IMPACT_THRESHOLD, total > 0):
case (True, True, _):
return RiskLevel.CRITICAL
case (True, _, _):
return RiskLevel.HIGH
case (_, True, _):
return RiskLevel.MEDIUM
case (_, _, True):
return RiskLevel.LOW
case _:
return RiskLevel.NONE
## ArchiMate vs другие нотации
Аспект
ArchiMate
C4 Model
UML
BPMN
Фокус
Enterprise целиком
Архитектура приложения
Дизайн кода
Бизнес-процессы
Слои
Business + App + Tech
4 уровня zoom
Нет слоёв
Pools/Lanes
Аудитория
Enterprise Architects, CTO
Разработчики, техлиды
Разработчики
Бизнес + IT
Сложность
Высокая
Низкая
Средняя-Высокая
Средняя
Стандарт
The Open Group
Нет (de facto)
OMG/ISO
OMG/ISO
Impact analysis
Да (основная ценность)
Нет
Нет
Нет
Связь бизнес-IT
Да
Частично
Нет
Частично
TOGAF и ArchiMate
ArchiMate часто используется вместе с TOGAF (The Open Group Architecture Framework).
TOGAF Phase
ArchiMate Viewpoint
Phase A: Architecture Vision
Stakeholder, Goal Realization
Phase B: Business Architecture
Business Process, Organization
Phase C: Information Systems
Application Cooperation, Data
Phase D: Technology Architecture
Technology Usage, Infrastructure
Phase E: Opportunities & Solutions
Migration, Implementation
Инструменты для ArchiMate
Инструмент
Тип
Лицензия
Archi
Desktop моделирование
Open Source
Sparx Enterprise Architect
Full EA Suite
Коммерческий
BiZZdesign
Enterprise SaaS
Коммерческий
ADOIT
EA Management
Коммерческий
draw.io
Универсальный (с шаблонами)
Бесплатный
Итоги
Концепция
Суть
Три слоя
Business, Application, Technology -- единая модель
Viewpoints
Разные представления для разных аудиторий
Relationships
Типизированные связи: serving, realization, flow
Impact Analysis
Главная ценность -- анализ влияния изменений
Strategy Layer
Связь архитектуры с бизнес-целями
TOGAF
ArchiMate -- визуальный язык для TOGAF-фреймворка
Совет: ArchiMate -- тяжёлая нотация. Используйте её только для enterprise-уровня (несколько систем, отделов, процессов). Для архитектуры одного приложения C4 Model будет проще и эффективнее. Начните с Archi (бесплатный) для первых экспериментов.