Recent Posts
Archives

Posts Tagged ‘csharp’

PostHeaderIcon [VoxxedDaysBucharest2026] Ports, Adapters, and the Independence of Business Logic: George Patrașcu on Hexagonal Architecture in Practice

Lecturer

George Patrașcu is a seasoned software engineer at eMAG/CTO with more than twenty years of professional experience, including significant time in architectural leadership positions. Currently contributing to the Invoice and Payments platform team, George plays an active role in shaping internal developer guidelines and promoting sound architectural practices throughout a large organization characterized by hundreds of autonomous teams and diverse technology stacks.

Abstract

Within expansive microservices ecosystems featuring autonomous teams, frequent deployments, and heterogeneous technologies, business logic commonly becomes entangled with infrastructure specifics, yielding systems that are difficult to maintain, evolve, or understand. George Patrașcu presents Hexagonal Architecture—also recognized as Ports and Adapters—as a pragmatic methodology for protecting core domain logic from external dependencies including relational databases, RESTful services, event streams such as Kafka, and various third-party integrations. Grounded firmly in production realities at eMAG, the session provides balanced coverage of conceptual foundations, detailed C# implementation examples, testing approaches, and honest discussion of trade-offs encountered in practice.

The Challenges of Traditional Layered Architectures in Evolving Systems

Conventional layered architectures consisting of presentation, business, and data access tiers deliver initial value for straightforward applications. However, sustained growth across hundreds of teams utilizing varied languages (Java, .NET, Python, Scala), communication mechanisms (REST, Kafka), and continuous integration practices exposes fundamental weaknesses.

Business rules gradually permeate multiple layers: service classes reference persistence entities directly, controllers embed data access logic, and external service contracts influence domain models. Bounded contexts, a cornerstone of Domain-Driven Design, demand careful translation through anti-corruption layers, yet traditional designs frequently fail to maintain clean separation.

Resulting issues include duplicated business logic across backend-for-frontend components, mobile client platforms, and core services; severely compromised unit testing due to pervasive infrastructure dependencies; and cascading changes whenever external contracts, database schemas, or framework versions evolve. Simple folder structures offer no enforceable boundaries, while multi-module projects introduce tedious mapping layers that increase cognitive load.

Core Concepts of Hexagonal Architecture: Ports, Adapters, and the Application Core

Hexagonal Architecture fundamentally inverts dependency direction to position the domain model at the center. Business logic and associated domain entities reside within the core, depending exclusively upon abstract ports rather than concrete implementations. Input ports, implemented by driving adapters (REST controllers, message consumers), expose use cases to external actors. Output ports, realized by driven adapters (repositories, external clients), allow the core to interact with infrastructure without awareness of underlying details.

This structure adheres strictly to the Dependency Inversion Principle: high-level policy (domain) remains independent of low-level mechanisms (infrastructure). The hexagonal representation visually encapsulates the core, with ports serving as interfaces through which adapters connect. Domain objects and language remain pure, expressed in business terms rather than technical artifacts.

Flexible structuring accommodates varying scales: monolithic single projects for initial simplicity, separation of adapters by technical domain (isolating payment processors or external APIs), or evolution from modular monoliths toward independent microservices. This “build modular from the start” philosophy supports rapid feature development followed by natural service extraction when boundaries emerge.

Implementation Details, Trade-offs, and Testing Strategies

Practical development begins with domain modeling. For a rescue fleet management system, an AssembleFleet input port defines the primary use case contract. Implementation within a domain service orchestrates inventory retrieval and selection logic expressed purely in business concepts.

Driven adapters address external complexities: a Swappy client manages API pagination, performs type coercions (string passenger counts to domain integers), and implements anti-corruption filtering for inconsistent partner data. Custom domain annotations (@DomainService) facilitate integration with dependency injection frameworks without introducing technical concerns into the core.

Persistence strategies favor rich domain models mapped via ORM capabilities (e.g., shadow properties, complex types), minimizing dual maintenance of entities. This preserves core purity while leveraging framework strengths.

Testing follows clear separation: exhaustive unit tests exercise domain logic in complete isolation, while integration tests utilize fakes and mocks for adapters, verifying translation correctness without external system dependencies.

Trade-offs warrant careful consideration. The pattern excels with rich domain models containing substantial behavior but may introduce unnecessary indirection for straightforward query-dominant services. Hybrid approaches applying hexagonal principles selectively to complex logic paths, combined with CQRS separation or vertical slice organization, often prove optimal. The speakers caution against architectural dogmatism—context, team maturity, and problem complexity should guide application extent.

Architectural fitness functions, implemented via tools like ArchUnit, provide automated verification of boundaries even when AI assistance generates code.

Implications for Autonomous Teams and Long-Term Maintainability

Within large organizations featuring hundreds of developers operating with significant autonomy and minimal centralized control, Hexagonal Architecture strikes an effective balance. Teams retain freedom in implementation details while benefiting from consistency promoted through architecture guilds and shared guidelines. The pattern facilitates technology migration, incremental refactoring, and clear delineation of responsibilities.

By maintaining domain logic independent of infrastructure specifics, services demonstrate greater resilience to external changes—whether API contract updates, database platform shifts, or new integration requirements. This aligns naturally with trunk-based development, feature flag strategies, and high-frequency deployment practices, enhancing overall organizational agility.

Not every service necessitates complete hexagonal purity. Selective application targeting areas of highest coupling delivers outsized returns in testability, evolvability, developer onboarding speed, and long-term maintenance costs.

Links:

PostHeaderIcon [NDCOslo2024] Running .NET on the NES – Jonathan Peppers

In a whimsical fusion of nostalgia and innovation, Jonathan Peppers, a principal software engineer at Microsoft, embarks on an audacious quest: running .NET on the Nintendo Entertainment System (NES), a 1985 gaming relic powered by a 6502 microprocessor. With a career steeped in .NET for Android and .NET MAUI, Jonathan’s side project is a playful yet profound exploration of cross-platform ingenuity, blending reverse engineering, opcode alchemy, and MSIL wizardry. His journey, shared with infectious enthusiasm, unveils the intricacies of adapting modern frameworks to vintage hardware, offering lessons in creativity and constraint.

Jonathan opens with a nod to the NES’s cultural cachet—a living room arcade for a generation. Its modest specs—less than 2 MHz, 52 colors, and minuscule cartridges—contrast starkly with today’s computational behemoths. Yet, this disparity fuels his ambition: to compile C# into 6502 assembly, enabling .NET to animate pixels on a 256×240 canvas. Through meticulous reverse engineering and bespoke compilers, Jonathan bridges eras, inviting developers to ponder the portability of modern tools.

Decoding the NES: Reverse Engineering and Opcode Orchestration

The NES’s heart, the 6502 microprocessor, speaks a language of opcodes—terse instructions dictating arithmetic and flow. Jonathan recounts his reverse-engineering odyssey, dissecting ROMs to map their logic. His approach: transform C#’s intermediate language (MSIL) into 6502 opcodes, navigating the absence of high-level constructs like methods or garbage collection. By crafting a custom compiler, he translates simple C# programs—think console outputs—into assembly, leveraging the NES’s 2KB RAM and 8-bit constraints.

Challenges abound: switch statements falter, branching logic stumbles, and closures remain elusive. Yet, Jonathan’s demos—a flickering sprite, a basic loop—prove viability. His toolkit, open-sourced on GitHub, invites contributions, with a human-crafted logo replacing an AI-generated predecessor. This endeavor, while not production-ready, showcases the power of constraints to spark innovation, echoing the NES’s own era of elegant simplicity.

Bridging Eras: Lessons in Cross-Platform Creativity

Jonathan’s experiment transcends mere novelty, illuminating cross-platform principles. The NES, with its rigid architecture, mirrors edge devices where resources are scarce. His compiler, mapping .NET’s abstractions to 6502’s austerity, mirrors modern efforts in WebAssembly or IoT. Structs, feasible sans garbage collection, hint at future expansions, while his call for pull requests fosters a collaborative ethos.

His reflection: constraints breed clarity. By stripping .NET to its essence, Jonathan uncovers universal truths about code portability, urging developers to question assumptions and embrace unconventional platforms. His vision—a C# Mario clone—remains aspirational, yet the journey underscores that even vintage hardware can host modern marvels with enough ingenuity.

Links:

PostHeaderIcon [DevoxxFR2012] .NET for the Java Developer: A Source of Inspiration? A Profound Cross-Platform Exploration of Language Design, Ecosystem Evolution, and the Future of Enterprise Programming

Lecturers

Cyrille Martraire stands as one of the most influential figures in the French software craftsmanship movement, having co-founded Arolla, a boutique consultancy that has redefined how enterprise teams approach code quality, domain-driven design, and technical excellence. With nearly two decades of experience building mission-critical financial systems at investment banks and fintech startups, Cyrille has cultivated a philosophy that places expressiveness, readability, and long-term maintainability at the heart of software development. He is the founder of the Software Craftsmanship Paris community, a regular speaker at international conferences, and a passionate advocate for learning across technological boundaries. His ability to draw meaningful insights from seemingly disparate ecosystems—such as .NET—stems from a deep curiosity about how different platforms solve similar problems, and how those solutions can inform better practices in Java.

Rui Carvalho, a veteran .NET architect and ASP.NET MVC specialist, brings a complementary perspective rooted in over fifteen years of web development across startups, agencies, and large-scale enterprise platforms. A fixture in the ALT.NET Paris community and a recurring speaker at Microsoft TechDays, Rui has witnessed the entire arc of .NET’s evolution—from the monolithic WebForms era to the open-source, cross-platform renaissance of .NET Core and beyond. His expertise lies not merely in mastering Microsoft’s tooling, but in understanding how framework design influences developer productivity, application architecture, and long-term system evolution. Together, Martraire and Carvalho form a dynamic duo capable of transcending platform tribalism to deliver a nuanced, humorous, and technically rigorous comparison that resonates deeply with developers on both sides of the Java–.NET divide.

Abstract

This article represents a comprehensive, elaborately expanded re-interpretation of Cyrille Martraire and Rui Carvalho’s landmark 2012 DevoxxFR presentation, “.NET pour le développeur Java : une source d’inspiration ?”, transformed into a definitive treatise on the parallel evolution of Java and C# and their mutual influence over nearly three decades of enterprise software development. Delivered with wit, mutual respect, and a spirit of ecumenical dialogue, the original talk challenged the audience to look beyond platform loyalty and recognize that Java and C# have been engaged in a continuous, productive exchange of ideas since their inception. From the introduction of lambda expressions in C# 3.0 (2007) to Java 8 (2014), from LINQ’s revolutionary query comprehension to Java’s Streams API, from async/await to Project Loom’s virtual threads, the presenters traced a lineage of innovation where each platform borrowed, refined, and occasionally surpassed the other.

This expanded analysis delves far beyond surface-level syntax comparisons to explore the philosophical underpinnings of language design decisions, the ecosystem implications of framework choices, and the cultural forces that shaped adoption. It examines how .NET’s bold experimentation with expression trees, dynamic types, extension methods, and Razor templating offered Java developers a vision of what was possible—and in many cases, what Java later adopted or still lacks.

EDIT
Updated for the 2025 landscape, this piece integrates the latest advancements: C# 13’s primary constructors and source generators, Java 21’s pattern matching and virtual threads, Spring Fu’s functional web framework, GraalVM’s native compilation, and the convergence of both platforms under cloud-native, polyglot architectures. Through rich code examples, architectural deep dives, performance analyses, and forward-looking speculation, this work offers not just a historical retrospective, but a roadmap for cross-platform inspiration in the age of cloud, AI, and real-time systems.

The Shared Heritage: A Tale of Two Languages in Constant Dialogue

To fully appreciate the depth of inspiration between Java and C#, one must first understand their shared origin story. Java was released in 1995 as Sun Microsystems’ answer to the complexity of C++, promising “write once, run anywhere” through the JVM. C#, announced by Microsoft in 2000, was explicitly positioned as a modern, type-safe, component-oriented language for the .NET Framework, but its syntax, garbage collection, exception handling, and metadata system bore an uncanny resemblance to Java. This was no coincidence. Anders Hejlsberg, the architect of C#, had previously designed Turbo Pascal and Delphi, but he openly acknowledged Java’s influence. As Cyrille humorously remarked during the talk, “C# didn’t just look like Java—it was Java’s younger brother who went to a different school, wore cooler clothes, and occasionally got better grades.”

This fraternal relationship manifested in a decade-long game of leapfrog. When Java 5 introduced generics in 2004, C# 2.0 responded with generics, nullable types, and anonymous methods in 2005. When C# 3.0 unveiled LINQ and lambda expressions in 2007, Java remained silent until Java 8 in 2014. When Java 7 introduced the invokedynamic bytecode in 2011 to support dynamic languages, C# 4.0 had already shipped the dynamic keyword in 2010. This back-and-forth was not mere imitation—it was a refinement cycle where each platform stress-tested ideas in production before the other adopted and improved them.

Lambda Expressions and Functional Programming: From Verbosity to Elegance

One of the most visible and impactful areas of cross-pollination was the introduction of lambda expressions and functional programming constructs. In the pre-lambda era, both Java and C# relied on verbose anonymous inner classes to implement single-method interfaces. A simple event handler in Java 6 looked like this:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Button clicked at " + e.getWhen());
    }
});

The equivalent in C# 2.0 was only marginally better, using anonymous delegates:

button.Click += delegate(object sender, EventArgs e) {
    Console.WriteLine("Button clicked");
};

But in 2007, C# 3.0 introduced lambda expressions with a syntax so clean it felt revolutionary:

button.Click += (sender, e) => Console.WriteLine("Clicked!");

This wasn’t just syntactic sugar. It was a paradigm shift toward functional programming, enabling higher-order functions, collection processing, and deferred execution. Rui demonstrated how this simplicity extended to LINQ:

var recentOrders = orders
    .Where(o => o.Date > DateTime.Today.AddDays(-30))
    .OrderBy(o => o.Total)
    .Select(o => o.CustomerName);

Java developers watched with envy. It took seven years for Java 8 to deliver lambda expressions in 2014, but when it did, it came with a more rigorous type system based on functional interfaces and default methods:

button.addActionListener(e -> System.out.println("Clicked!"));

The Java version was arguably more type-safe and extensible, but it lacked C#’s expression-bodied members and local functions.

EDIT:
In 2021, Java 21 has closed the gap further with pattern matching and unnamed variables, but C# 13’s primary constructors in records remain unmatched:

public record Person(string Name, int Age);

LINQ: The Query Comprehension Revolution That Java Never Fully Embraced

Perhaps the most profound inspiration from .NET—and the one Java has still not fully replicated—is LINQ (Language Integrated Query). Introduced in C# 3.0, LINQ was not merely a querying library; it was a language-level integration of query comprehension into the type system. Using a SQL-like syntax, developers could write:

var result = from p in people
             where p.Age >= 18
             orderby p.LastName
             select new { p.FirstName, p.LastName };

This syntax was compiled into method calls on IEnumerable<T>, but more importantly, it was extensible. Providers could translate LINQ expressions into SQL, XML, or in-memory operations. The secret sauce? Expression trees.

Expression<Func<Person, bool>> predicate = p => p.Age > 18;
var sql = SqlTranslator.Translate(predicate); // "SELECT * FROM People WHERE Age > 18"

Java’s Streams API in Java 8 was the closest analog:

List<Person> adults = people.stream()
    .filter(p -> p.getAge() >= 18)
    .sorted(Comparator.comparing(Person::getLastName))
    .map(p -> new PersonDto(p.getFirstName(), p.getLastName()))
    .toList();

But Streams are imperative in spirit, lack query syntax, and cannot be translated to SQL without external tools like jOOQ. Cyrille lamented: “Java gave us the pipeline, but not the language.”

Asynchronous Programming: async/await vs. the Java Journey

Concurrency has been another arena of inspiration. C# 5.0 introduced async/await in 2012, allowing developers to write asynchronous code that looked synchronous:

public async Task<string> FetchDataAsync()
{
    var client = new HttpClient();
    var html = await client.GetStringAsync("https://example.com");
    return Process(html);
}

The compiler transformed this into a state machine, eliminating callback hell. Java’s journey was more fragmented: Futures, CompletableFuture, Reactive Streams, and finally Project Loom’s virtual threads in Java 21:

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    return executor.submit(() -> client.get(url)).get();
}

Virtual threads are a game-changer, but they don’t offer the syntactic elegance of await. As Rui quipped, “In C#, you write synchronous code that runs asynchronously. In Java, you write asynchronous code that hopes to run efficiently.”

Web Frameworks: From WebForms to Razor and the Templating Renaissance

Rui traced .NET’s web framework evolution with particular passion. The early 2000s were dominated by ASP.NET WebForms, a drag-and-drop, event-driven model that promised rapid development but delivered ViewState bloat, postback hell, and untestable code. It was, in Rui’s words, “a productivity trap disguised as a framework.”

The community rebelled, giving rise to ALT.NET and frameworks like MonoRail. Microsoft responded with ASP.NET MVC in 2009, embracing separation of concerns, testability, and clean URLs. Then came Razor in 2010—a templating engine that felt like a revelation:

@model List<Person>
<h1>Welcome, @ViewBag.User!</h1>
<ul>
@foreach(var p in Model) {
    <li>@p.Name <em>(@p.Age)</em></li>
}
</ul>

No XML. No JSP-style scriptlets. Just C# and HTML in harmony. Java’s JSP, JSF, and even Thymeleaf felt antiquated by comparison. But in 2020, Spring Boot with Thymeleaf or Micronaut Views has narrowed the gap, though Razor’s layout system and tag helpers remain superior.

The Cutting Edge in 2025: Where Java and C# Stand Today

EDIT:
C# 13 and .NET 9 continue to innovate with source generators, record structs, and minimal APIs:

var builder = WebApplication.CreateBuilder();
var app = builder.Build();
app.MapGet("/", () => "Hello World");
app.Run();

Java 21 counters with pattern matching for switch, records, and virtual threads, but lacks native metaprogramming. Projects like Spring Fu and Quarkus are pushing functional and reactive paradigms, but the expressive gap remains.

Conclusion: Inspiration Without Imitation

Martraire and Carvalho’s core message endures: Java and .NET are not rivals—they are collaborators in the advancement of managed languages. The inspiration flows both ways, and the future belongs to developers who can transcend platform boundaries to build better systems.

EDIT:
In 2025, as cloud-native, AI-augmented, and real-time applications dominate, the lessons from this 2012 dialogue are more relevant than ever.

Links