Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
LEVELUP 2015 - Enterprise software schaalbaar m...
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Michiel Overeem
November 26, 2015
Technology
330
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
LEVELUP 2015 - Enterprise software schaalbaar maken met Service Fabric
hoe AFAS zijn next gen ERP platform ontwikkelt
Michiel Overeem
November 26, 2015
More Decks by Michiel Overeem
See All by Michiel Overeem
Reminiscing a crazy journey: building an event sourced ERP system
overeemm
0
250
EventSourcing.Live 2021 - A system with thousands of event types
overeemm
0
440
EventSourcing.Live 2021 - Event system evolution - A Scientific Study on Event Sourcing, Lessons from Industry
overeemm
0
410
DDDVienna - 103 years of event sourcing experience
overeemm
0
490
DDDVienna - Applying event sourcing and CQRS in a large ERP system
overeemm
1
1.4k
Landelijk Architectuur Congres - The dark side of event sourcing: managing data conversion
overeemm
0
450
Dutch .NET Group Meetup - Building an event sourced system in .NET
overeemm
0
640
Drukwerkdeal.nl Developer Meetup - Event Sourcing After Launch
overeemm
0
310
DDDEurope 2018 - Event Sourcing After Launch
overeemm
5
2.6k
Other Decks in Technology
See All in Technology
QAタスクをスキル化したいときに考えること
aomoriringo
0
120
AIとハーネスで育てるトランスコンパイラ / 20260722 Yasushi Katayama
shift_evolve
PRO
4
1.1k
なぜMIXIはゼロトラスト基盤として クラウドフレアを選んだのか - Cloudflare Peer Point SASE User Voices
mixi_engineers
PRO
2
120
NYC Summit 2026 におけるAmazon Bedrock AgentCore のアップデート
ren8k
1
250
StepFunctionsとGraphRAGを活用した暗黙知活用のためのRAG基盤
yakumo
0
180
AI時代におけるエンジニアの新たな役割──FDEとクオリアの探求/登壇資料(戸井田 裕貴)
hacobu
PRO
0
630
QAと開発の両側から進める AI活用 -QAプロセスAI支援ツールキットと Inner Loop / Outer Loopの取り組み-
legalontechnologies
PRO
2
330
AI Native なプロダクト組織の立ち上げ方 : 生産性 100 倍への挑戦
mikesorae
0
1.7k
オートマトンと字句解析でRoslynを読む
tomokusaba
0
110
副作用のある Lambda でも Lambda Power Tuning は使えるのか / lambda-power-tuning-side-effects
koukihosaka
2
160
41歳でAWSが好きすぎてITエンジニアになったおっさんの話
yama3133
1
790
カメラ×AIで挑む「ホワイト物流」― 車両管理、自動化の壁と突破口【SORACOM Discovery 2026】
soracom
PRO
0
170
Featured
See All Featured
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
A better future with KSS
kneath
240
18k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
270
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
180
BBQ
matthewcrist
89
10k
The agentic SEO stack - context over prompts
schlessera
0
850
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.8k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.1k
エンジニアに許された特別な時間の終わり
watany
108
250k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Scaling GitHub
holman
464
140k
Building the Perfect Custom Keyboard
takai
2
820
Transcript
Enterprise software schaalbaar maken met Service Fabric hoe AFAS zijn
next gen ERP platform ontwikkelt
360+ medewerkers (4 locaties) 10.000 klanten (bedrijven) 1.500.000 gebruikers 80
miljoen euro omzet (2014) AFAS Software
AFAS Profit Maandelijks 1.500.000 loonstroken HRM, CRM, financieel, order management,
project management, workflow, ...
AFAS Online 1780 cores, 25 TB RAM, 180 TB SSD
storage 10.000 concurrent RDP gebruikers 200.000 unieke gebruikers per maand 1.500.000 API calls per dag
Software kun je definiëren in plaats van programmeren.
Modelleer het bedrijf met zijn processen
Sla het model op in een definitie
Lees de definitie in een generator
Lever de resulterende applicatie uit
None
client command-systeem query-systeem event bus
Command public class CreateArticleCommand : Command { public Guid ArticleId
{ get; set; } public string Description { get; set; } public decimal Price { get; set; } } Event public class ArticleCreatedEvent : Event { public Guid ArticleId { get; set; } public string Description { get; set; } public decimal Price { get; set; } } Query public class GetArticleQuery : Query { public Guid ArticleId { get; set; } }
client query-systeem event bus command-systeem
AggregateRoot - Handle public void Handle(CreateArticleCommand command) { if(command.Price <=
0) { throw new CommandValidationException("Price should be greater than 0."); } if(string.IsNullOrEmpty(command.Description) || command.Description.Length > 20) { throw new CommandValidationException("Description is mandatory, " + "and cannot be longer than 20 characters."); } RaiseEvent(new ArticleCreatedEvent(command.ArticleId, command.Description, command.Price)); } AggregateRoot - Apply private void Apply(ArticleCreatedEvent @event) { _saleable = true; _price = @event.Price; }
client query-systeem event bus
CommandHandler private void Handle(CreateArticleCommand command) { Repository.ExecuteOn<ArticleAggregateRoot>(command.ArticleId, command); } AggregateRootRepository
- InMemory public virtual async Task ExecuteOn<T>(Guid aggregateId, Command command) where T: AggregateRoot { T aggregateRoot = LoadAggregateRoot<T>(aggregateId); aggregateRoot.Handle(command); await SaveAndDispatchEvents(aggregateRoot); }
1 3 7 4 8 2 8 6 5 1
3 1 4 6 2 7 6 5 4 8 3 7 2 5
“Actors are isolated, single-threaded components that encapsulate both state and
behavior.”
1 3 7 4 8 2 8 6 5 1
3 1 4 6 2 7 6 5 4 8 3 7 2 5
service systeem reliable collections communicatie actors service API service systeem
reliable collections communicatie service API
client query-systeem event bus
AggregateRootRepository - InMemory public virtual async Task ExecuteOn<T>(Guid aggregateId, Command
command) where T: AggregateRoot { T aggregateRoot = LoadAggregateRoot<T>(aggregateId); aggregateRoot.Handle(command); await SaveAndDispatchEvents(aggregateRoot); } AggregateRootRepository – Service Fabric public override async Task ExecuteOn<T>(Guid aggregateId, Command command) { var actor = ActorProxy.Create<IAggregateRootActor>(new ActorId(aggregateId), "App"); await actor.ExecuteOn(typeof(T).AssemblyQualifiedName, command.ToJson()); }
AggregateRootActor public async Task ExecuteOn(string aggregateRootType, string commandJson) { var
aggregateRoot = LoadAggregateRoot(aggregateRootType); var command = Deserialize(commandJson); aggregateRoot.Handle(command); await SaveAndDispatchEvents(aggregateRoot); }
één Stateless Actor Type Range partitions voor load balancing Actor
Id is het Id van de AggregateRoot
client command-systeem event bus query-systeem
QueryModelBuilder private void Handle(ArticleCreatedEvent @event) { Repository.Add(@event.ArticleId, new JObject( new
JProperty("Article", @event.ArticleId), new JProperty("Description", @event.Description), new JProperty("Price", @event.Price))); } QueryHandler private JObject Handle(GetArticleQuery query) { return Repository.Get(query.ArticleId); }
client command-systeem event bus
client command-systeem event bus
QueryModelBuilder Service public async Task Handle(string eventJson) { var queue
= await StateManager.GetOrAddAsync<IReliableQueue<string>>("qmbQueue"); using(ITransaction tx = StateManager.CreateTransaction()) { await queue.EnqueueAsync(tx, eventJson); await tx.CommitAsync(); } }
QueryModelBuilder Service protected override async Task RunAsync(CancellationToken cancellationToken) { var
queue = await StateManager.GetOrAddAsync<IReliableQueue<string>>("qmbQueue"); while(true) { using(ITransaction tx = StateManager.CreateTransaction()) { ConditionalResult<string> dequeueReply = await queue.TryDequeueAsync(tx); if(dequeueReply.HasValue) { string message = dequeueReply.Value; _queryModelBuilder.Handle(Deserialize(message)); await tx.CommitAsync(); } } } }
één Stateful Service Type Named partitions voor load balancing Partition
name is het type van de QueryModelBuilder
None
Voor vragen, discussie etc:
[email protected]
- @michielovereem - https://linkedin.com/in/movereem CQRS
+ Service Fabric • Sluit ontzettend goed aan bij onze architectuur. • Betrouwbaarheid, schaalbaarheid en onderhoudsgemak. • Portable cloud hosting is mogelijk. https://github.com/AFASSoftware/CQRS-Microservices