Welcome

Powered By:
Powered by Subtext

Blog Stats

  • Blogs - 713
  • Posts - 31438
  • Articles - 310
  • Comments - 185600
  • Trackbacks - 217007

Bloggers (posts, last update)

Latest Posts

Dapr - The end

Throughout this series of articles, we have explored the various features and abstractions provided by Dapr, such as service invocation, pub/sub, state management, configurations, secrets, resiliency, and observability. However, it's important to note that there are even more capabilities within Dapr that we haven't covered, including actors, workflow management, distributed locks, and hosting.

Given the extensive nature of Dapr and its continuous growth, it's challenging to capture every aspect in a limited series of articles. To gain a deeper understanding, I encourage you to explore the official documentation, read relevant books, and explore additional online resources like GitHub projects and YouTube channels dedicated to Dapr. These resources can provide you with in-depth insights into Dapr's features and practical implementations.

Before diving into Dapr and incorporating it into your projects, it's crucial to have a solid understanding of microservices and distributed architecture in general. Consider the trade-offs involved and evaluate whether this architectural approach aligns with your business needs. Assess your team's technical skills and expertise, as both development and infrastructure aspects can become complex when working with distributed systems.

In terms of implementation, it's often beneficial to adopt a "Monolith First" approach, where you start with a monolithic architecture or, better, with a modular monolith. This approach allows you to experience the benefits of modularization and gain insights into the specific areas that can benefit from microservices. Eventually, you can decompose the monolith into a full-fledged microservices solution, leveraging the power of Dapr to simplify the development and management of your distributed system.

Thank you for following along with this series, and best of luck on your Dapr journey!

The end

posted @ 17/04/2024 11:37 by Martino Bordin

Dapr - Observability

Monitoring a distributed system is important to identify potential failures, security issues, bottlenecks and in general to understand how our application is performing to apply remediations (that could be scaling or redesigning some parts). In a distributed system, with several interconnected components, a dynamic environment, and unreliable networks, this becomes challenging.

Dapr, with his runtime running as a "sidecar", is the central point where the instrumentation of monitoring takes place to have distributed tracing, logging, metrics, and health check; as everything in Dapr, we can just configure these features using a YAML file.

In our sample, we'll use Zipkin to collect tracing information, just by specifying the Zipkin endpoint and the sampling rate (0= disable trace, 1 trace all)

apiVersion: dapr.io/v1alpha
kind: Configuration
metadata:   name: daprConfig
spec:   tracing:     samplingRate: "1"     zipkin:       endpointAddress: "http://zipkin:9411/api/v2/spans"1

In the Zipkin UI, we can see both traces and services dependencies

No alt text provided for this image

No alt text provided for this image

Dapr can produce structured logs in plain text (the default, that also sends it to stdout) or JSON (better for storing them in external services like ELK).

No alt text provided for this image

We can also use Open Telemetry Collector, Jaeger, and other tools like New Relic and App Insights.

See you in the next article for the last one of this series.

posted @ 25/03/2024 11:46 by Martino Bordin

Dapr - Resiliency

As we know, in a distributed system we have to take into account more complexity and deal with its fallacies. Since several moving parts could potentially be broken or be unreachable, we must design upfront our application with resiliency in mind, to minimize the impact of bad events and continue operating.

Dapr gives us out-of-the-box features that allow our applications to handle temporary failures.

All we have to do is to create a YAML file where we define policies and targets.

Policies are how we want to handle errors:

  • Timeout,  to terminate operations after defined intervals
  • Circuit breaking, to 'open' the circuit when there are several errors in order not to flood the system and give it time to restart the failing service
  • Retry, to retry failed operations

Targets are where we want to apply such policies:

  • Apps, our services (defined in docker-compose in our sample)
  • Components, the different building blocks of Dapr
  • Actors, the actor operations (not used in our sample)

I won't post the full YAML specification in this article, but I prepared a full example with comments in the GitHub repository.

Check it out here.

Once done, we configure the Dapr sidecar to load the resiliency configuration using the -resources-path argument.

In the next article, we'll see observability in Dapr

posted @ 05/02/2024 11:27 by Martino Bordin

Dapr - Secret management

Dapr Secrets management is a feature that provides a secure and scalable way to manage application secrets, such as API keys, passwords, and tokens. You can use different cloud providers and environments such as Azure Key Vault, AWS Secrets Manager, HashiCorp Vault, or Kubernetes Secrets.

In our example, we'll use a JSON file (not recommended in production!) to store the following secrets:

"httpbindingtoken": "my auth token",   "ConnectionStrings": {     "SqlServer": "Server=sqlserver;Database=CustomersDb;User Id=sa;Password=admin12345!;TrustServerCertificate=true",     "PostgreSQL": "Server=postgres;Port=5432;Database=ProductsDb;User Id=admin;Password=admin12345;",     "MongoDb": "mongodb://mongo:27017"   },   "MongoDb": {     "DatabaseName": "OrderDb",     "OrdersCollectionName": "Orders"   }
}

We have two way to retrieve them:

  • declaratively, in the components spec files
  • programmatically, using the Dapr APIs

We had a glimpse of the first approach in the previous article, where we configured the Http Output Binding component.

Quick recall:

apiVersion: dapr.io/v1alph
kind: Component
metadata:
  name: httpbinding
spec:
  type: bindings.http
  version: v1
  metadata:
  - name: url
    value: http://echorestbot.azurewebsites.net/microdelivery
  - name: securityToken
    secretKeyRef:
      name: httpbindingtoken
      key: httpbindingtoken
  - name: securityTokenHeader
    value: "Authorization"
auth:
  secretStore: localsecretstore

Here we are configuring some metadata and you may notice that for the item securityToken instead of directly inserting the value, we are declaring that we want to use the key httpbindingtoken retrieve in localsecretstore.

Localsecretstore is the name of the component for secret management, configured as usual in the YAML file

apiVersion: dapr.io/v1alpha
kind: Component
metadata:   name: localsecretstore
spec:   type: secretstores.local.file   version: v1   metadata:   - name: secretsFile     value: components/secrets.json   - name: nestedSeparator     value: ":"

Here we are basically declaring where we store the secrets (in the file secrets.json) and what is the separator character for nested configuration (colon is the default).

To use it programmatically, we have to add the Nuget package Dapr.Extensions.Configuration in our project and register it with the following lines of code

var builder = WebApplication.CreateBuilder(args)
builder.Configuration.AddDaprSecretStore(
"localsecretstore", 
new DaprClientBuilder().Build(), 
new[] { ":" });

Once done, we can access our secret simply using the standard IConfiguration.

public OrdersRepository(IConfiguration configuration
{
   var clientSettings = MongoClientSettings
                           .FromConnectionString(configuration
                           .GetConnectionString("MongoDb"));    var client = new MongoClient(clientSettings);    var database = client
                       .GetDatabase(configuration
                           .GetValue<string>("MongoDb:DatabaseName"));

   this.orders = database.GetCollection<Order>(configuration
                           .GetValue<string>("MongoDb:OrdersCollectionName"));
})

That's it!

In the next article, we'll see how to leverage the resiliency feature of Dapr.

posted @ 12/01/2024 16:29 by Martino Bordin

Dapr - Pub/sub &amp; Output Binding

In the last article, once created an order we published a message, called OrderSubmittedIntegrationEvent with all the relevant information.

How can we subscribe to this event in other services?

In Dapr there are two ways:

  • declaratively, where subscriptions are defined in an external file
  • programmatically, where subscriptions are defined in our code

We're going to use the second method in the Notifications and Shipping microservices along with the Output binding building block

What is an Output binding? It's a way to invoke external resources just by passing a payload and additional metadata.

Let's see them in action!

No alt text provided for this image

Notifications microservice

The notification microservice will have just one method that is going to use the SMTP output binding to send an email to inform the customer that the order has been confirmed and what discount has been applied.

Here is the stripped version of the code, check GitHub for the complete one.

[HttpPost
[Topic("rabbitmqpubsub", "OrderSubmittedEventTopic")]
public async Task<ActionResult> OnOrderSubmittedEventAsync(
    OrderSubmittedIntegrationEvent orderSubmittedIntegrationEvent)
{
	var stringBuilder = new StringBuilder();
	stringBuilder.AppendLine($"Hello {orderSubmittedIntegrationEvent.CustomerFirstName} {orderSubmittedIntegrationEvent.CustomerLastName}<br>");
	stringBuilder.AppendLine($"Your order <strong>#{orderSubmittedIntegrationEvent.OrderId.ToString()[..6]}</strong> has been shipped.<br><br>");
	stringBuilder.AppendLine($"Your CRAZY DISCOUNT is <strong>#{orderSubmittedIntegrationEvent.TotalDiscount}%</strong>!<br><br>");

	var message = stringBuilder.ToString();
	var metadata = new Dictionary<string, string>
	{
		{ "emailTo", orderSubmittedIntegrationEvent.CustomerEmail },
		{ "subject", $"Order Shipped!" },
		{ "priority", "1" }
	};
	await this.daprClient
                        .InvokeBindingAsync(
                           "smtpbinding", 
                           "create", 
                           message, 
                           metadata);

	return Ok();
}]

The first thing you can notice is that we're using the Topic attribute to subscribe to the topic OrderSubmittedEventTopic on the rabbitmqpubsub component.

Dapr is going to invoke this method once a new message is published to that topic, and we receive the payload as a parameter (OrderSubmittedIntegrationEvent). We then use the payload to create an email and send it using the InvokeBindingAsync, passing as a parameter the name of the binding component (smtpbinding), the body of the email, and some metadata (subject, mail address, priority).

Of course, we have to configure the binding in the YAML specification:

apiVersion: dapr.io/v1alpha
kind: Component
metadata:   name: smtpbinding
spec:   type: bindings.smtp   version: v1   metadata:   - name: host     value: "smtp4dev"   - name: port     value: "25"   - name: emailFrom     value: "no-reply@microdelivery.com"

For testing purposes, I'm using an email server called smtp4dev hosted in docker to send and check emails.

No alt text provided for this image

The smtp4dev User Interface

Now let's see the shippings service.

No alt text provided for this image

Shipping microservice

All the logic is in the method OnOrderSubmittedEvent, where we're subscribing to the topic OrderSubmittedEventTopic, and we're performing an HTTP request using the HTTP Output Binding (to simulate a call to an external supplier). Once done, we publish the OrderShippedIntegrationEvent that will be handled by the Orders microservice to mark the order as shipped.

[HttpPost
[Topic("rabbitmqpubsub", "OrderSubmittedEventTopic")]
public async Task<ActionResult> OnOrderSubmittedEvent(
   OrderSubmittedIntegrationEvent orderSubmittedIntegrationEvent)
{
	await this.daprClient.InvokeBindingAsync(
                                    "httpbinding", 
                                    "post", 
                                     orderSubmittedIntegrationEvent);


	var orderShippedIntegrationEvent = new OrderShippedIntegrationEvent() 
                      { 
                        OrderId = orderSubmittedIntegrationEvent.OrderId, 
                        ShippedAtUtc = DateTime.UtcNow 
                      };

	await daprClient.PublishEventAsync(
                                        "rabbitmqpubsub", 
                                        "OrderShippedEventTopic", 
                                        orderShippedIntegrationEvent);

	return Ok();
}]

As in the notifications microservice, we use the InvokeBindingAsync, this time to call the HTTP endpoint, passing the orderSubmittedIntegrationEvent as payload.

The binding is configured with the following YAML specification:

apiVersion: dapr.io/v1alpha
kind: Component
metadata:
  name: httpbinding
spec:
  type: bindings.http
  version: v1
  metadata:
  - name: url
    value: http://echorestbot.azurewebsites.net/microdelivery
  - name: securityToken
    secretKeyRef:
      name: httpbindingtoken
      key: httpbindingtoken
  - name: securityTokenHeader
    value: "Authorization"
auth:
  secretStore: localsecretstore

Here you see that we want to perform a POST request to the URL http://echorestbot.azurewebsites.net/microdelivery (it's a simple API I built that gives you back the request details). We are also passing a token in the "Authorization" header; the value of the token is read by using the secret management build block (more info in the next article).

Here is an example of the request we made to the EchoRest endpoint, where you can see the payload containing the order's detail:

{   "RequestIdentifier": "14085696-218e-42f2-8966-7a0b37b506ea",   "Date": "2023-05-01T20:02:07.2657896Z",   "Url": "http://echorestbot.azurewebsites.net/microdelivery",   "Body": "{\"orderId\":\"307998cc-53c9-46cf-9dca-06df25419496\",\"customerId\":1,\"customerFirstName\":\"Joe\",\"customerLastName\":\"Doe\",\"customerEmail\":\"joe.doe@email.com\",\"orderLineItems\":[{\"productId\":1,\"productName\":\"Tomato salad\",\"quantity\":3,\"price\":8,\"discountedPrice\":8},{\"productId\":2,\"productName\":\"Margherita\",\"quantity\":3,\"price\":6,\"discountedPrice\":6}],\"totalDiscount\":0,\"eventId\":\"823132ef-197a-4fc3-8da4-175966bae8dc\"}",   "Headers": [     {       "Key": "Accept",       "Value": "application/json; charset=utf-8"     },     {       "Key": "Host",       "Value": "echorestbot.azurewebsites.net"     },        {       "Key": "Authorization",       "Value": "my auth token"     },     {       "Key": "Content-Type",       "Value": "application/json; charset=utf-8"     },     {       "Key": "Content-Length",       "Value": "412"     }          ],   "Method": "POST",   "Path": {     "Value": "/microdelivery",     "HasValue": true   }
}

After calling the sample endpoint, we're going to publish an OrderShippedIntegrationEvent that will be handled by the Order's microservice to update the order, just subscribing to that event using the Topic attribute we already saw.

[HttpPost("Ship")
[Topic("rabbitmqpubsub", "OrderShippedEventTopic")]
public async Task<ActionResult> OnOrderShippedEvent(OrderShippedIntegrationEvent orderShippedIntegrationEvent)
{
	var order = await this.orderRepository

                     .GetOrderAsync(orderShippedIntegrationEvent.OrderId);
	if (order is null)
	{
		return NotFound();
	}

	order.ShippedAtUtc = orderShippedIntegrationEvent.ShippedAtUtc;
	await this.orderRepository.UpdateOrderAsync(order);

	return Ok();
}]

In the next article, we'll discuss about secret management with DAPR.

posted @ 06/12/2023 17:53 by Martino Bordin

Water in Wine with C#

A common need when working with images is to convert arrays containing pixels from a type to another.

For example an uint[] to byte[] conversion is common, but very slow and memory intensive.

Here the fastest method I developed for real-time applications:


The code is unsafe (additional testing recommended) and may not work with future CLR Runtimes, but it's pretty readable and fast.

In few words the code is changing the type of the array by overwriting the CLR Method Table and then resizing the array in a similiar way. A new array (of the desired type) pointing to the original one is then returned. The original data is never copied and this makes this method very fast to execute.

Here how to use the function CastArray:


An array containing a 4K 32 bit image can be casted with a great speedup compared to an Array copy with standard solutions such as Buffer.BlockCopy.

The drawback is that now we have overwritten the original array size and if we use the original array, we should manually ensure that we are not reading over it's end.

The byte[] is actually a reference array and changing it will change the uint[] (and the opposite), but this can avoid further casts.

Note: As I already done in the electro-logic blog, new posts starting from this one are only in English to enable more people to read them, but you can still contact me or leave a comment in Italian if you prefer.

That's all for today

posted @ 24/11/2023 22:51 by Leonardo

Dapr - Service invocation &amp; Pub/sub

Dapr service discovery & invocation is a feature that allows services to discover and call other services using HTTP or gRPC protocols, providing a simple and consistent way to invoke them.

Communication is also

  • secure (with mutual mTLS authentication)
  • resilient (with configurable retry\circuit breaker\timeout policies)
  • traced and metered (using common protocols)
  • controlled (we can use ACL to restrict accesses\permissions to some APIs)
  • balanced (using round-robin)

The pub/sub is a messaging pattern where a message is published by a sender to a topic, and all the subscribers who have subscribed to that topic will receive a copy of the message. This allows loosely coupled communication between services, as the sender does not need to know who the receivers are or how many there are, and the subscribers do not need to know who the publisher is.

The pub/sub API in DAPR provides a set of APIs that can be used to implement this pattern and allows developers to use different message brokers like Apache Kafka, RabbitMQ, Azure Service Bus, and many others. The API is platform-agnostic and offers an at least-once message delivery guarantee.

Let's see how we can use both functionalities in the orders microservices.

No alt text provided for this image

As always, we have a standard API controller where we inject the DaprClient.

The relevant code is in the SubmitOrder.

Essentially, what we want to do is:

  • retrieve customer info (calling the customer's microservice)
  • apply the current valid discount (calling the discount microservice)
  • retrieve products info
  • create and save a new order
  • publish a message OrderSubmittedIntegrationEvent

[HttpPost("Submit")
[ProducesResponseType(typeof(Order), (int)HttpStatusCode.Created)]
public async Task<ActionResult> SubmitOrder(SubmitOrderRequest request)
{
	var customerInfo = await daprClient
                               .InvokeMethodAsync<CustomerInfo>(
                                HttpMethod.Get,
                                "microdelivery-customers-api", 
                                $"customers/{request.CustomerId}");

	if (customerInfo is null)
	{
		throw new Exception($"Customer {request.CustomerId} not found");
	}

	var discount = await daprClient
                           .InvokeMethodAsync<int>(
                            HttpMethod.Get, 
                            "microdelivery-discount-api", 
                            "discount");
	var order = new Order
	{
		CustomerId = customerInfo.Id,
		CustomerFirstName = customerInfo.FirstName,
		CustomerLastName = customerInfo.LastName,
		CustomerEmail = customerInfo.Email,
		TotalDiscount = discount
	};
	var orderLineItems = new List<OrderLineItem>();
	foreach (var requestOrderLineItem in request.OrderLineItems)
	{
		var productInfo = await daprClient
                                  .InvokeMethodAsync<ProductInfo>(
                                  HttpMethod.Get, 
                                  "microdelivery-products-api", 
                                  $"products/{requestOrderLineItem.ProductId}");
		if (productInfo is null)
		{
			throw new Exception($"Product {requestOrderLineItem.ProductId} not found");
		}

		var discountedPrice = discount == 0 ? productInfo.Price : productInfo.Price - (productInfo.Price * ((double)discount / 100));
		var orderLineItem = new OrderLineItem
		{
			ProductId = productInfo.Id,
			ProductName = productInfo.Name,
			Price = productInfo.Price,
			DiscountedPrice = discountedPrice,
			Quantity = requestOrderLineItem.Quantity
		};
		orderLineItems.Add(orderLineItem);
	}
	order.OrderLineItems = orderLineItems;
	
    await orderRepository.CreateOrderAsync(order);

	var orderSubmittedIntegrationEvent = new OrderSubmittedIntegrationEvent
	{
		OrderId = order.Id,
		CustomerId = order.CustomerId,
		CustomerFirstName = order.CustomerFirstName,
		CustomerLastName = order.CustomerLastName,
		CustomerEmail = order.CustomerEmail,
		TotalDiscount = order.TotalDiscount,
		OrderLineItems = order.OrderLineItems.Select(oli => 
         new OrderSubmittedIntegrationEventLineItem 
         { 
          ProductId = oli.ProductId, 
          ProductName = oli.ProductName, 
          Quantity = oli.Quantity, 
          Price = oli.Price, 
          DiscountedPrice = oli.DiscountedPrice 
         })
	};

    await daprClient.PublishEventAsync(
       "rabbitmqpubsub", 
       "OrderSubmittedEventTopic", 
       orderSubmittedIntegrationEvent);

	return CreatedAtAction(nameof(GetOrder), new { id = order.Id }, order);
}

For the service invocation, we use the method InvokeMethodAsync<T> of the DaprClient.

We have to specify the HTTP Verb to use, the application id of the other service (we set it on the sidecar configuration in the docker-compose file), and the method name. If the call is successful, we obtain the response of type T. That's simple.

To publish a message, we use the method PublishEventAsync<T> of the DaprClient.

We have to specify the name of the pubsub component, the topic where we want to publish the message, and the message payload.

In our example, we're going to use RabbitMq, so here is the component specification:

apiVersion: dapr.io/v1alpha
kind: Component
metadata:   name: rabbitmqpubsub
spec:   type: pubsub.rabbitmq   version: v1   metadata:   - name: connectionString     value: "amqp://rabbit:5672"   - name: hostname     value: rabbit    - name: username     value: guest   - name: password     value: guest 

In the next article, we'll see how other services can subscribe to the published event in order to perform their logic.

posted @ 04/11/2023 13:39 by Martino Bordin

Dapr - Input Binding and Configuration

Dapr binding is a mechanism that allows connections between components and external services by providing a common interface for communication, without needing to know about each other's implementation details.

It allows components to send and receive messages over a range of transport protocols and message brokers, like RabbitMQ and Kafka, Azure Event Grid, AWS SNS, Azure Service Bus, and Amazon SQS. You can also integrate with Twitter, SMTP, SendGrid, HTTP, or CRON.

Dapr has a building block also to manage and retrieve the application configurations (settings, connection strings, identifiers, etc) just by configuring the related component.

At the moment, the supported configuration stores are Redis, Postgres, and Azure App configuration.

Let's see how we can use both functionalities in the discount microservices.

No alt text provided for this image

First of all, we have to reference an additional NuGet package: Dapr.Extensions.Configuration

Once done, just go to the Program.cs file and add these lines of code, where we declare the config store and the list of keys we want to retrieve.

var builder = WebApplication.CreateBuilder(args

var client = new DaprClientBuilder().Build();
builder.Configuration     .AddDaprConfigurationStore(
       "redisconfigstore", 
       new List<string>() { "CrazyDiscountEnabled" }, 
       client, 
       TimeSpan.FromSeconds(20))     .AddStreamingDaprConfigurationStore(
       "redisconfigstore", 
       new List<string>() { "CrazyDiscountEnabled" }, 
       client, 
       TimeSpan.FromSeconds(20));)

Please note that AddDaprConfigurationStore will make the first call to load the config with the given keys, while AddStreamingDaprConfigurationStore will keep watching for changes and update local configurations.

Here's the main code of the discount controller:

[ApiController
[Route("[controller]")]
public class DiscountController : ControllerBase
{
	private readonly DaprClient daprClient;
	private readonly IConfiguration configuration;

	public DiscountController(
       DaprClient daprClient, 
       IConfiguration configuration)
	{
		this.daprClient = daprClient;
		this.configuration = configuration;
	}


	[HttpPost("/discountcronbinding")]
	public async Task<ActionResult> UpdateDiscount()
	{
		var crazyDiscountEnabled = configuration
                          .GetValue<bool>("CrazyDiscountEnabled");

		if (!crazyDiscountEnabled)
		{
			return Ok();
		}

		var random = new Random(Guid.NewGuid().GetHashCode());
		var discount = random.Next(1, 30);

		await daprClient.SaveStateAsync("redisstore", "CrazyDiscountValue", discount);
		return Ok();
	}


	[HttpGet]
	public async Task<int> GetDiscount()
	{
        var discount = 0;       
        var crazyDiscountEnabled = configuration
                          .GetValue<bool>("CrazyDiscountEnabled");      
        if (crazyDiscountEnabled)         
        {   
           discount = await daprClient
                   .GetStateAsync<int>("redisstore", "CrazyDiscountValue");         
        } 
 
         return discount;
	}
}]

We have a standard API Controller where we inject the DaprClient and the standard IConfiguration object that allows us to retrieve the settings.

The UpdateDiscount method checks if the configuration with the key CrazyDiscountEnabled is enabled. If yes, we're going to generate a random discount and save it using the state management we already know.

How is configured the Config store? Using the component specification, of course!

apiVersion: dapr.io/v1alpha
kind: Component
metadata:   name: redisconfigstore
spec:   type: configuration.redis   metadata:   - name: redisHost     value: redis:63791


As you can see we're connecting to Redis, so we can enable the crazy discount feature by running the following command in the Redis CLI

SET CrazyDiscountEnabled true

Now the random discount calculation is enabled, but when and who is calling this API?

We're going to use the CRON input binding, which will trigger a POST request based on the CRON expression we specify.

Here is our configuration:

apiVersion: dapr.io/v1alpha
kind: Component
metadata:   name: discountcronbinding
spec:   type: bindings.cron   version: v1   metadata:   - name: schedule     value: "* * * * * *"

We set the CRON expression in order to trigger every second. The POST will call a method with the same name as the binding (discountcronbinding in our case). So, basically, we're calculating a new random discount every second (if enabled by config).

We can retrieve the current valid discount just by performing a GET request to the Discount endpoint.

No alt text provided for this image

In the next article, we'll see the Pub\Sub building block.

posted @ 10/10/2023 10:37 by Martino Bordin

Dapr - State management

Dapr allows the storage of durable data (key\value pairs) across multiple sessions and services.

With Dapr State Management, you can:

  • Save and retrieve state using different stores with 2 levels of consistency (strong and eventual)
  • Use a consistent API to perform operations, abstracting away the implementation details.
  • Implement caching (with TTL) to improve performance.
  • Filter\Sort\Page state entries

Let's see how we can use it in the customer's microservices.

No alt text provided for this image

The customers' microservice persists data in MS SQL Server and caches data in MongoDB

Here's the code:

public class CustomersController : ControllerBase
{
	private readonly ILogger<CustomersController> logger;
	private readonly DaprClient daprClient;
	private readonly ICustomersRepository customerRepository;


	public CustomersController(ILogger<CustomersController> logger, DaprClient daprClient, ICustomersRepository customerRepository)
	{
		this.logger = logger;
		this.daprClient = daprClient;
		this.customerRepository = customerRepository;
	}

	[HttpGet]
	[ProducesResponseType(typeof(IEnumerable<Customer>), (int)HttpStatusCode.OK)]
	public async Task<ActionResult<IEnumerable<Customer>>> GetCustomers()
	{
		var customers = await GetCustomersFromCache();
		return Ok(customers);
	}

	private async Task<IEnumerable<Customer>> GetCustomersFromCache()
	{
		var customers = await daprClient
                              .GetStateAsync<IEnumerable<Customer>>(
                               "mongostore", 
                               "GetCustomers");

		if (customers == null)
		{
			customers = await customerRepository
                              .GetCustomersAsync();
			await daprClient
                            .SaveStateAsync("mongostore", 
                                            "GetCustomers", 
                                            customers);
		}

		return customers;
	}
}
  

We have a standard API Controller where we inject the DaprClient (view the previous article to see the NuGet package required and how to register it).

In the GetCustomersFromCache method, we use the GetStateAsync to check if we previously cached the data. If yes, we just return it; if no, we load it (using the customer repository), cache it using the SaveStateAsync and return it.

Please note that we are specifying where we want to save our state by passing the store name (in our case "mongostore"). But where is it defined?

If you remember in the previous article we configured the sidecar with the component path; that's the folder where we store the configurations of our components.

Therefore, we just have to create under that folder a new YAML file with the following configuration

apiVersion: dapr.io/v1alpha
kind: Component
metadata:   name: mongostore
spec:   type: state.mongodb   version: v1   metadata:   - name: host     value: "mongo:27017"

The value "mongostore" in the metadata section is the store name we have to use in our code (I set it as "mongostore", but you can follow your preferred naming convention). We also specify how to connect to the MongoDB store.

Where can you find all these settings? In the official documentation, of course!

Now we have all the information to use another state store (i.e. Redis) as I've done in the products microservice.

The products' microservice persists data in PostgreSQL and cache data in Redis

The relevant piece of code is similar to the previous one:

private async Task<IEnumerable<Product>> GetProductsFromCache(
{
	var products = await daprClient
                         .GetStateAsync<IEnumerable<Product>>(
                                                              "redisstore", 
                                                              "GetProducts");
	if (products == null)
	{
		products = await this.productRepository.GetProductsAsync();
		await daprClient.SaveStateAsync("redisstore", "GetProducts", products);
	}

	return products;
})

To configure the Redis state store, we create the following file in the component's folder

apiVersion: dapr.io/v1alpha
kind: Component
metadata:   name: redisstore
spec:   type: state.redis   version: v1   metadata:   - name: redisHost     value: redis:6379   - name: redisPassword     value: ""

To test both customers' and products' microservices, I prepared 2 .http files with the sample GET requests, so we can use Visual Studio.

Just start the solution (using the docker-compose) and executes both requests.

When executing the GET /customers, the result will be cached in Mongo

In the source code on GitHub, you can see also how to delete the state and how to set a TTL of the cached value (otherwise cache will never expire!).

In the next article, we'll see the Configuration and Binding building blocks

posted @ 06/09/2023 17:54 by Martino Bordin

Dapr - Create a Dapr service

In order to use Dapr with C#, you just have to create a new ASP.NET Core Web API Project and reference the Nuget package Dapr.AspNetCore

Once done, just go in the Program.cs and chain the method AddDapr() after the method AddControllers():

builder.Services.AddControllers().AddDapr();

In addition to that, just call these methods to register the Pub\Sub building block:

app.MapSubscribeHandler();

app.UseCloudEvents();

At this point, you can inject the DaprClient, in your controller or services, to interact with the several APIs of Dapr.

public CustomersController(DaprClient daprClient)
{
  this.daprClient = daprClient;
}


We then can run the application along with Dapr sidecar using the CLI

dapr run --app-id microdelivery.customers.api --app-port 8000 -- dotnet run

But, since we're going to create several microservices, I prefer the docker-compose approach. Here the YAML file for the customer's microservice (microdelivery.customers.api) and its sidercar (microdelivery.customers.api.dapr).


version: '3.4

services:
  # Customers Api & Sidecar
  microdelivery.customers.api:
    container_name: microdelivery.customers.api
    image: ${DOCKER_REGISTRY-}microdeliverycustomersapi
    build:
      context: .
      dockerfile: src/MicroDelivery.Customers.Api/Dockerfile
    depends_on:
      - sqlserver
      - redis
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
    ports:
      - "8000:80"

  microdelivery.customers.api.dapr:
    container_name: microdelivery.customers.api.dapr
    image: "daprio/daprd:latest"
    network_mode: "service:microdelivery.customers.api"
    depends_on:
      - microdelivery.customers.api
    command: ["./daprd",
      "-app-id", "microdelivery-customers-api",
      "-app-port", "80",
      "-resources-path", "/components",
      "-config", "/configuration/configuration.yaml",
      "-log-level", "debug"
      ]
    volumes:
      - "./dapr/components/:/components"
      - "./dapr/configuration/:/configuration"'

The sidecar is a docker image (daprio/daprd) attached to the network of the customer's microservices where we execute the daprd command, passing some parameters like the app id that identifies our app, the app port, what is the components\config path to customize our microservices behavior and the log level.

We can then just run the application with Visual Studio that will create the two containers and we're ready to debug it!

In the next article, we'll see the first building block: State management

posted @ 03/07/2023 09:19 by Martino Bordin

Dapr - The application scenario

In this series of articles (source code is available on GitHub), we'll develop a microservices application for an imaginary food delivery company called "MicroDelivery".

We'll have several services, each with its own logic, that will interact via RPC call or pub\sub messaging and we'll see how to use Dapr to build it.


⚠️ Disclaimer

My goal here is to give you a birds-eye view of the different features of Dapr, not to guide you step by step on building an application from scratch, nor to explain the single details of Dapr (there's the official website and several books for that!)

I won't explain why and when to adopt a microservices architecture and related pros\cons, nor we'll talk about any design\integrations\architectural patterns.

☁️We're not going to use any Cloud Provider (only on-prem docker services), but of course, the nature of Dapr allows you to easily switch from on-prem to the cloud.

⚒️ The application leverages several technologies(Redis, RabbitMq, SqlServer, PostgreSQL, MongoDB, Zipkin) and shows you how it's simple to use them together with Dapr. Of course, in the real world, you need to analyze your requirements and understand if these tools are useful for your needs along with their strong and weak points.


Customers Microservice

No alt text provided for this image

Customers Microservice

It's a CRUD microservice to manage customers' data.

It persists its data in SQL Server using Entity Framework and caches them in MongoDB, using Dapr State block

Products Microservice

No alt text provided for this image

Products Microservice

It's a CRUD microservice to manage products' data.

It persists its data in PostgreSQL using Entity Framework and caches them in Redis using Dapr State block

Orders Microservice

No alt text provided for this image

Orders Microservice

It's a microservice that receives the order requests, performs some, publishes a message (OrderSubmittedEvent) to notify other services, and receives a message (OrderShipped) to mark an order as shipped.

It persists its data in MongoDB, calls Discount\ Customers\Products microservices using service-to-service DAPR block, and send\received message in RabbitMQ using Dapr Pub\Sub block,

Discount Microservice

No alt text provided for this image

Discount Microservice

It's a microservice that, if enabled by configuration, calculates a random discount (very funny, isn't it?) that remains valid until the next recalculation.

The (re)calculation is triggered by a Dapr CRON Binding, and the configuration is stored on the Redis configuration block. It will be invoked by the Orders microservice using Dapr service-to-service communication,

Notifications Microservice

No alt text provided for this image

Notifications Microservice

It's a microservice that receives the message OrderSubmittedEvent and sends a confirmation email to customers, using Dapr SMTP binding.

Shipping Microservice

No alt text provided for this image

Shipping Microservice

It's a microservice that receives the message OrderSubmittedEvent and performs an HTTP call to an external Webhook, using Dapr HTTP binding and reading the Bearer Token from Dapr Secret store. It will also publish an OrderShipped event.

Here is the full application diagram

123

A full diagram of the sample application

The dotted links are the calls to Dapr Sidecar, performed using the C# SDK.

In the next article, we'll see how to set up Dapr within our project.

posted @ 05/06/2023 18:09 by Martino Bordin

Dapr - An introduction to the runtime

Dapr, standing for Distributed Application Runtime, is an open-source event-driven runtime that simplifies the building of resilient microservices-based applications.

It's designed to work with any programming language, any infrastructure, and any cloud provider (Azure, AWS, GCP) and it's part of the Cloud Native Computing Foundation (CNCF) project.

Its main goal is to abstract the complexity of distributed systems and allows developers to focus on business logic rather than the underlying infrastructure (warning: just because the complexity is 'hidden' doesn't mean you should not be aware of what it is and how you would solve it).

These abstractions are called 'Building blocks' and provide APIs for common functionalities like State Management, Service Discovery & Invocation, Publish & Subscribe, Observability, and many others.

No alt text provided for this image

The implementation of a building block is called 'component' and there are several of them ready to use, all configurable using external YAML files and all easily interchangeable.

For example, for the publish\subscribe feature, we have components for RabbitMQ, Redis, AWS SNS/SQS, or GCP Pub/Sub.

No alt text provided for this image

Dapr will run as an external process\container (called Sidecar) and our application will interact with it using HTTP\GRPC or the SDKs available for the main programming languages (.NET, Java, Node, Python, Go).

No alt text provided for this image

We can also use a CLI and a Dashboard to initialize, run, and tests Dapr applications.

No alt text provided for this imageNo alt text provided for this image

You can read the full documentation on the official website.

In the next article, we'll see how we can build a sample application using DAPR.

posted @ 29/05/2023 18:20 by Martino Bordin

WPF ed i 0.01 DPI mancanti

Una caratteristica di WPF e' l'indipendenza dalla risoluzione, in particolare le immagini bitmap vengono automaticamente ridimensionate a 96 DPI, valore di default storico di Windows.

Un'immagine 512x512 pixel a 72 DPI verra' quindi ridimensionata a 682.5 x 682.5 pixel circa. Questo succedera' indipendentemente dai DPI impostati nel sistema.

Utilizzando software professionali di editing di immagini come Adobe Photoshop, Gimp, etc.. e' possibile salvare i nostri capolavori con una risoluzione DPI specifica. Questa informazione (DpiX e DpiY) non modifica di fatto la dimensione in pixel dell'immagine, ma e' un attributo che viene salvato nei metadati del file e che puo' essere per esempio utile in fase di stampa... e in WPF.

Soddisfatti della nostra creazione, impostiamo quindi 96 DPI, certi che la nostra immagine non verra' mai e poi mai ridimensionata, ed aggiungiamo il seguente codice XAML alla nostra pagina:


La nostra immagine e' stata inserita in un controllo Viewbox a scopo dimostrativo per evitare ridimensionamenti dovuti a limiti di spazio della Window, selezionando il controllo Image notiamo pero' qualcosa di strano nel pannello delle proprieta' di Visual Studio.


L'immagine e' stata ridimensionata da WPF! Il nostro castello di carte e' cascato dalle fondamenta, manca circa 0.1 pixel per lato!

Questo puo' essere un problema trascurabile dal punto di vista grafico e prestazionale, ma cerchiamo ad ogni modo di capire cosa sta succendo.

Eseguendo il seguente codice C#


scopriamo una triste realta' che stravolgera' presto tutte le nostre certezze. L'immagine e' di fatto a 96.01 DPI!

Adobe e' impazzita? un Easter Eggs di Gimp? Un complotto di Visual Studio?

Vediamo quindi di fare chiarezza, il formato PNG salva la risoluzione in un chunk chiamato Physical pixel dimensions (pHYs) composto da tre campi:

- Pixels per unit, x axis (4 bytes, unsigned integer)
- Pixels per unit, y axis (4 bytes, unsigned integer)
- Unit specifier (1 byte)

dove lo standard definisce come unita' valide solamente il metro o l'unita' indefinita. Il metro e' definito anche dal SI (Sistema Internazionale di unita' di misura), quindi a prima vista ha senso impiegarlo in uno standard al posto dei piedi o di altre unita' meno blasonate.

Utilizzando il metro stiamo quindi parlando di DPM (Dot per Meter). Un DPM equivale a 0.0254 DPI e sfortuna vuola che i campi prevedano solamente valori interi.

3779 DPM equivalgono a circa 95.99 DPI mentre 3780 a circa 96.01 DPI. Non esiste modo di specificare 96 DPI precisi, 0.01 DPI saranno sempre mancanti.

NB: Molti software arrotondano i valori di DPI visualizzati, prestare quindi sempre la massima attenzione.

Questo e' un bel dilemma per WPF, che di default usa proprio 96 DPI per le immagini, anche se a dire il vero non ho visto insurrezioni popolari in questi anni.

Un software utile per analizzare (e modificare) i metadati dei file PNG e' TweakPNG, dove a prima vista i nostri sogni di perfezione DPI-eggianti tornano, per poi svanire all'amara realta' con un doppio click.




NB: Questo problema non affligge il formato TIFF dove l'unita' di risoluzione puo' essere impostata in pollici (inch).

Per motivi di orgoglio piu' che tecnici non vogliamo pero' convertire tutte le nostre icone al formato TIFF, cosa possiamo dunque fare?

La matematica non lascio molto scampo, ma la fantasia ha ancora qualche carta da giocare.

Per risolvere il problema possiamo procedere in due modi:

- cancellare il chunk pHYs (che e' opzionale) se creato dall'app di editing grafico.

- impostare ad "unspecified units" l'unita' di misura. I valori X ed Y verranno ignorati.

Entrambe queste soluzioni forzeranno a 96 DPI esatti la risoluzione dell'immagine in WPF ed eviteranno qualsiasi ridimensionamente.

Personalmente preferisco utilizzare utility come PNGGauntlet che eliminano i chunk opzionali ed ottimizzano la dimensione del file (senza perdita di dati). Molto utile anche per evitare errori "App manifest references .. which is larger than the maximum image file size." durante il packaging di applicazioni UWP.

Avete mai notato che i 96 DPI esatti non esistono nei formati PNG e BMP? Dite la vostra nei commenti ed un saluto a tutti!

NB: Gli stessi concetti posso applicarsi ad altre tecnologie basate su XAML come ad esempio UWP

posted @ 24/03/2023 02:18 by Leonardo

Per chiudere il cerchio 2004-2021 sulla Complessità



Un video per condividere le lezioni che ho imparato e maturato,  a partire dalla proma Keynote di Joseph Pelrine al Agile Day nel 2004.




Applicazioni pratiche della Complessità nello sviluppo software & di prodotti digitali - Luca Minudel from Italian Agile Movement on Vimeo.

Questa sessione illustra un panorama di pratiche ispirate dalla Human-Complexity che possono essere usate ogni giorno nello sviluppo software e di prodotti digitali. Viene presentato anche un nuovo approccio per adottare Human-Complexity thinking per farsi strada verso il nuovo modo di pensare attraverso la pratica.


posted @ 21/12/2021 12:31 by Luca Minudel

Summer 2021 reading list

Quest'anno come reading-list propongo un libro che ho scritto.
Non è come un romanzo di Camilleri, ma anche per i piu esperti di Agile contiene concetti e modelli sconosciuti a molti.
E per molti che lavorano in varie aziende contiene idee che potrebbero essere 20 anni avanti a alcuni dei modi di pensare consolidati.

Vado oltre il fatto che trovo di cattivo gusto fare questa forma di auto-promozione qui solo per il fatto che il contenuto di questo libro è maturato in un contesto particolare all'estero e credo che condividere queste idee nuove e avanzate con chi opera in un contesto prevalentemente Italiano possa essere molto utile: Living Complexity




posted @ 21/07/2021 20:30 by Luca Minudel

Cloud Champions: storie di cloud, raccontate dai suoi protagonisti

Stasera inizio una nuova avventura, si chiama "Cloud Champions" e consiste in una serie di interviste (trasmesse in live streaming) a persone insieme alle quali cercherò di comunicare perché "cloud" non è "hosting 2.0" ma molto di più.

È per questo motivo che gli episodi saranno sia tecnici sia "business" o esperienziali perché, ormai, "cloud" è sia una piattaforma che permette a sviluppatori ed imprenditori di avvalersi di una piattaforma elastica nelle performance e nei costi, sia l'infrastruttura che permette a noi utenti finali di goderci contenuti in streaming o il gaming in rete.

L'elenco completo (e costantemente aggiornato) degli episodi è disponibile qui, e stasera partiamo col botto insieme a Davide Mauri, senior PM di Azure SQL presso Microsoft Corporation.

A vale della live, nella quale sarà possibile interagire con l’ospite mediante la chat, pubblicheremo le registrazioni anche in formato podcast sui seguenti canali:

  • Listen on Apple Podcasts
  • Ascolta su Spreaker
  • Ascolta su Spotify

posted @ 16/03/2021 14:44 by Andrea Saltarello

www.geniodelmale.info è tornato. So long, and thanks for all the fish!

Il momento è arrivato. Dopo tanti anni, migliaia di post, migliaia di commenti, ho deciso di rilanciare www.geniodelmale.info, di cominciare a bloggare di nuovo in inglese, e di salutare il blog di UgiDotNet, che tanto mi ha dato per la mia crescita personale e professionale.
Un grazie di 💝 ad Andrea, per avermi sopportato e, soprattutto, supportato tutti questi anni.
Ho copiato e importato (manualmente) tutti i post che c'erano qui su UgiDotNet, e anche tutti quelli del vecchio blog fatto con Dexter Blog Engine, spero di non aver perso nulla 😊.

Ciao a tutti, spero vogliate seguirmi anche di la.

posted @ 01/03/2021 14:19 by Lorenzo Barbieri

App Postepay

E' arrivato il momento del Cashback di Stato!
Ma, a parte questo, decido di rispolverare la vecchia Postepay anche per l'euro in più di cashback promesso che, scoprirò, è applicato davvero in pochi negozi e, pertanto, inutile.

Scarico l'app Postepay, configuro la mia carta e... "Servizio temporaneamente non disponibile".
Riprovo nei giorni successivi... nulla.

Provo a telefonare al numero verde dedicato a Postepay e mi perdo nel labirinto delle scelte senza considerare un messaggio di qualche minuto relativo al cashback di Stato che non posso saltare.
Pian piano imparo a memoria le scelte da fare ma ogni volta, a fine percorso, mi viene detto che il centralino è intasato e occorre riprovare più tardi.

Dopo due giorni, riesco a parlare con un operatore e ad aprire un ticket.
Nei giorni successivi sarà impossibile riuscire ad avere informazioni relative a quel ticket.
La cosa più fastidiosa sarà l'assistente digitale: AI pessima. Qualcuno ha mai provato ad usarla? Basta dire "operatore" e lei ti risponderà: "Sei sicuro, non mi vuoi dare un'altra possibilità?" e da lì un giro infinito.

Dalla pagina dell'app scrivo una pessima recensione e mi viene risposto di scrivere il problema alla mail dello sviluppatore. Fatto.
Premetto che l'app era aggiornata all'ultima versione e ho provato anche ad eliminarla e reinstallarla. Alla fine, tramite la pagina Facebook di Postepay mi viene detto di creare il PosteId per usufruire dei servizi online. Lo creo anche se dopo scoprirò che questa notizia era del tutto falsa.

Anche su Facebook, tramite messaggio privato, non ottengo risposta.
Mi sembra abbastanza grave non avere assistenza per un prodotto finanziario (scadente): potrei capire se si trattasse della tessera del supermercato!

Alla fine ricevo una chiamata da un tecnico dopo una settimana di mancato utilizzo dell'app che risolve il problema dicendo che il numero di telefono associato al mio account (fin dall'iscrizione anni fa) era associato anche ad un altro account (creato anni fa). Non potevo disassociarlo in autonomia ma avrei comunque dovuto chiamare loro (che non rispondevano). Ovviamente è caduta la linea mentre verificavo la risoluzione del problema con il tecnico al telefono e non sono stato richiamato.

Direi molte cose da rivedere in questa gestione.
Sicuramente "Servizio temporaneamente non disponibile" non è un messaggio accettabile quando i problemi sono altri!

posted @ 15/12/2020 15:25 by Emanuele Prato

Merp incluso nel GitHub Archive Program

Da qualche anno, nel tempo "libero", sviluppo Merp, una applicazione open source che scelsi di creare per dotare la seconda edizione del mio libro "Microsoft .NET: Architecting Applications for the Enterprise" di un esempio vero di DDD/CQRS/ES invece dei soliti "snippettini".

Nel tempo, Merp ha suscitato un po' di interesse e i suoi 115 fork e 347 star lo hanno portato ad essere incluso nel "GitHub Archive Program"per, testuali parole, essere "preservato per le future generazioni"  insieme a mostri sacri quali .NET Core, Angular, Hadoop, NodeJS, Rails ed altri ancora.

Speriamo abbiano scelto un commit decente: 1000 anni di insulti e sfottò nuocerebbero ulteriormente alla mia sindrome dell'impostore.

2020 GitHub Archive Program

posted @ 29/07/2020 19:02 by Andrea Saltarello

Ci vediamo questa sera alla sessione italiana di Build?

Questa sera sarò in compagnia di un po’ di amici alla sessione italiana di Build, potete iscrivervi qui: https://mybuild.microsoft.com/sessions/ebfcb753-c90c-4743-931d-c0f84b303543?source=sessions

Vedremo vari aspetti legati alle community, io in particolare parlerò di diversità ed inclusione.

La mia parte sarà intorno alle 18.30, ci vediamo tra poco!

posted @ 20/05/2020 14:44 by Lorenzo Barbieri

Lightning talk a Microsoft Build

Prima che scoppiasse la pandemia, l'ultimo evento "fisico" al quale ho partecipato come speaker è stato il Microsoft Ignite The Tour: 2 talk, dei quali uno tecnico e l'altro, a me molto più caro, dedicato alla tecnologia quale strumento di inclusione.

Quando mi è stato proposto di tornare sul tema, oltretutto a Microsoft Build, ho accettato al volo: ne parlerò quindi stasera verso le 18:30 con un lightning talk all'interno dello slot "Supporting the (other) communities".

Ci vediamo qui.

Logo Microsoft Build 2020

#MicrosoftBuild #a11yDays #inclusione #accessibility

posted @ 20/05/2020 11:23 by Andrea Saltarello

Cronache di AWS: prospettiva .NETvski

Non tutto il lock-down vien per nuocere: approfittando della logistica favorevole, del tempo risparmiato non dovendo fare avanti e indietro tra casa/ufficio/clienti e non volendo dedicare la totalità del tempo libero al binge watching, ho finalmente deciso di dedicarmi in modo organizzato ad un tema che avevo nel backlog da un po’: un bell’approfondimento dei miei skill AWS.

Per rendere quanto più real world possibile il mio approfondimento, mi sono dato un obbiettivo pratico da affiancare alle attività didattiche: prendere Merp e farlo girare su AWS; ciò, principalmente, per due motivi; il primo è che il progetto usa una serie significativa di componenti (server web, coda di messaggi, SQL Server, MongoDB, …) e, quindi, pubblicarlo comporta dover toccare una gamma abbastanza ampia di servizi quali, ad esempio:

  • RDS for SQL Server per i read model
  • Amazon DocumentDB per gli event store che on-premises sarebbero ospitati da MongoDB
  • EC2 per vari motivi (SSH, compute, …)
  • AmazonSQS come trasporto per Rebus
  • Beanstalk per le applicazioni web e Lamba per endpoint HTTP

Il secondo motivo è che, disponendo di un progetto reale, ho potuto impostare un percorso di application modernization composto da step successivi che, partendo dal solito lift&shift, mi portassero, per così dire, per “aspera a PaaS”.

Ne è nata una esperienza, ancora in corso, piena sia di momenti “?” sia di momenti “a-ah!” ed ho pensato di documentarla in una serie di contenuti che pubblicherò su UGIdotNET, e composta da articoli che documenteranno il percorso del progetto (es: definizione di un environment ASP .NET Core per il supporto ad AWS) e tip che, invece, saranno dedicati a necessità puntali (es: come faccio a connettermi con il mio client MongoDB al cluster DocumentDB?).

Il primo è qui: chiunque voglia seguire il percorso potrà forkare Merp e smanettare (il progetto è sotto licenza AGPL 3) in prima persona.

Proverò anche, parallelamente ad articoli e tip, a postare sul blog per una serie di considerazioni più individuali che al progetto in sè puntando a condividere, ad esempio, risorse utili trovate durante il percorso: tool, pubblicazioni, webinar, tutto quanto mi sia sembrato utile e non sia direttamente contenuto nelle mie pubblicazioni.

Proprio relativamente ai webinar, ne segnalo un paio che seguirò prossimamente: soprattutto “Serverless and Container .NET Best Practices” mi torna particolarmente utile.

posted @ 07/05/2020 12:38 by Andrea Saltarello

Summer 2019 reading & watching list

Ecco una selezione di post e video per l'estate :-)


Agile & Agility



-  Un aggiornamento al linguaggio datato del Manifesto Agile (utile solo per Training, non è una evoluzione del manifesto ... un altro post su questo tema stà per arrivare): http://www.smharter.com/blog/2019/06/12/freshened-up-agile-manifesto/


Fraintendimenti e incomprensioni del Agile

- SAFe il framework che più e più esperti sconsigliano: https://www.linkedin.com/pulse/safe-lean-agile-modern-management-theory-luca-minudel/

- Figuracce delle grandi aziende di consulenza che si improvvisano esperte di Agile: https://www.linkedin.com/posts/lucaminudel_misunderstoodagile-misappliedagile-missoldagile-activity-6564484028391583744-1yKP



 Libro

- Enterprise Agility: Being Agile in a Changing World by Sunil Mundra (ThoughtWorks): https://www.amazon.co.uk/Enterprise-Agility-Being-Agile-Changing/dp/1788990641


Video

- How F1 teams crack technical debt https://youtu.be/4C2hOC0WbcA

- Cultural Change: Startups wish they were bigger, big companies dream of being Startup-ish. So what? https://youtu.be/Ue2XXWOulM0






posted @ 03/08/2019 20:08 by Luca Minudel

Benvenuto Surface Book 2

Da qualche giorno sto “giocando” col mio nuovo Surface Book 2, devo dire che mi trovo veramente bene.

Certo, un paio di cose non mi hanno convinto, e magari in un secondo momento farò un post per parlarne, ma l’esperienza è ottima. Erano anni che usavo un form factor diverso, quello del Surface Pro, dal mitico #1 comprato da un collega perché ancora non si trovava in Italia, al Pro 2, comprato il giorno di lancio negli US assieme alla Docking Station INTROVABILE, al Pro 3, il vero salto di qualità. Tutti questi me li ero comprati io, lasciando il PC aziendale a casa. Dovevo avere un Surface Pro.

Poi è arrivato il Pro 4 di mamma Microsoft, che mi ha accompagnato negli ultimi tre anni con grandi successi, ma avendo quasi terminato la batteria. Per fortuna avevo comprato il “mega power bank” di Li-zone, una bomba.

Ora si riparte, con un PC molto più potente, un po’ più pesante, con uno schermo che mi piace molto.

posted @ 31/01/2019 01:05 by Lorenzo Barbieri

In giro: prossimi eventi [UPDATED]

Ispirato da Lorenzo, ho deciso di iniziare a pubblicare sul blog gli eventi nei quali sarò presente:
  • Il 31 gennaio sarò presente, in qualità di speaker, alla tappa milanese del Microsoft Ignite Tour; nello specifico, terrò  la sessione "Azure tales: real world Event Sourcing" (codice BRK3589) alle 9:30 nella sala "Amber 7" e, con gli amici (in rigoroso ordine alfabetico) Daniele, Laurent e Martina sarò uno dei sedicenti esperti protagonisti dell'Ask The Expert "Here's how the cloud will solve all your problems" (codice THR2506) alle 17:10 nel Theater 2
  • Sempre il 31 gennaio, ma IMHO merita un punto a parte, insieme ad Enos terrò alle 10:50 la sessione “Implementing bots and Alexa skills with Azure Cognitive Services and the Microsoft Bot Framework” nel meetup organizzato da UGIdotNET presso il Microsoft Ignite Tour
  • L'1 febbraio parteciperò all'evento "Ignite your Azure", il primo incontro organizzato dal neonato Azure Meetup Milano
  • il 7 febbraio, presso la Microsoft House, sarò speaker presso l’evento “Azure Experience: migrare le tue applicazioni? E’ possibile!” durante il quale terrò due sessioni dedicate, rispettivamente, ai pattern ricorrenti nella migrazione di applicazioni verso Azure ed ad illustrare alcune delle opportunità abilitate dalla adozione del cloud a valle della migrazione. L’intenzione, per intenderci, consiste nello sfatare un po’ dei miti per i quali “migrare in cloud la mia applicazione? Impossibile, perché bla bla bla bla, …”

L’invito è sempre il solito: se ci sarete ed avete voglia di fare 2 chiacchiere, non esitate a farvi avanti.

posted @ 28/01/2019 18:02 by Andrea Saltarello

Prossimi eventi dove incontrarci

lorenzo_600x600Ogni tanto aggiorno il blog con i prossimi eventi dove possiamo incontrarci, e rieccoci con i primi eventi (in Italia) di questo semestre:

- 31 gennaio e 1 febbraio, Microsoft Ignite Tour, al 90% sarò presente almeno uno dei due giorni come partecipante.

- sabato 2 marzo,global diversity CFP day, assieme a Mauro Servienti abbiamo organizzato un evento dedicato ad aspiranti speaker o a persone che vogliano migliorarsi, in un ambiente inclusivo e accogliente, indipendentemente dal tipo di talk, dal fatto che la persona appartenga o meno ad una minoranza, insomma, un posto PROTETTO dove farsi le ossa. Ci saranno due sessioni e poi chi vorrà potrà mettersi alla prova.

L’elenco completo con gli eventi anche fuori dall’Italia lo trovate qui.

posted @ 23/01/2019 11:47 by Lorenzo Barbieri

Ritorno al futuro

Il mio primo post su UGI e' di Ottobre 2003, l'ultimo di Luglio 2009....

Il primo corrisponde piu' o meno al periodo in cui fui invitato da Andrea a parlare di Agile ad un evento (chi c'era? era il 17 Settembre 2003!).

L'ultimo piu' o meno a quando, ormai da 4 anni a Londra, stare dietro alle attivita' anche in Italia era diventato difficile e infatti e' lo stesso anno in cui cominciai a pensare di fare un passo indietro anche nell'organizzazione dell'Italian Agile Day.

Sono passati 9 anni da allora e sono di nuovo qui :-)

L'idea originale era di aprire una filiale Italiana della mia azienda UK ma, come si dice, la vita segue spesso percorsi misteriosi e mi ritrovo ora in Managed Designs, socio oltre che amico di quello stesso Andrea che mi invito' a parlare ad un evento UGI 15 anni fa :-D

La cosa ha riportato alla mente parecchi eventi UGI memorabili ma per me quello che resta una spanna sopra gli altri rimane l'evento al cinema Arcadia con Andrea e Fabio vestiti da Spiderman nel 2004. Peccato non trovare le foto ma sono certo qualcuno qui le abbia da qualche parte...

Spero di avere l'occasione di rivedere di persona tanti vecchi amici, possibilmente al prossimo evento UGI ;-)

posted @ 05/11/2018 19:20 by Marco Abis

Salvare una presentazione di PowerPoint riducendo la dimensione delle immagini

Oggi avevo bisogno di salvare una presentazione PowerPoint che occupava una quarantina di Mb in un “formato” compatibile con la mail. Certo, avrei potuto salvarla in PDF, ma il destinatario aveva bisogno del file PPTX.

Dopo qualche “giro” nella UI, ho trovato l’opzione giusta:

Annotazione

posted @ 08/10/2018 17:13 by Lorenzo Barbieri

Prossimi eventi dove incontrarci&hellip;

Sarà un autunno abbastanza intenso, questo del 2018

Spero di non essermi dimenticato di nessun evento (già confermato, ho ancora qualche call for paper in attesa di risposta ):

Vediamo come andranno le CFP di cui aspetto ancora il risultato, nel caso farò un nuovo post con tutte le date e i link aggiornati. Volete invitarmi a parlare di Azure, DevOps o di Public Speaking? Scrivetemi a geniodelmale AT outlook PUNTO com

posted @ 13/09/2018 20:53 by Lorenzo Barbieri

dbeaver



Free multi-platform database tool for developers, SQL programmers, database administrators and analysts. Supports all popular databases: MySQL, PostgreSQL, MariaDB, SQLite, Oracle, DB2, SQL Server, Sybase, MS Access, Teradata, Firebird, Derby, etc. 

posted @ 06/07/2018 21:14 by Alessandro Gervasoni